我想使用netty编写一个简单的程序来代理浏览器发送的http请求。我觉得可以分为3个步骤
题:
这是我学习netty的第一天,所以请尽量回答简单。非常感谢。
public class Server {
public static void main(String[] args) throws InterruptedException {
final int port = 8888;
// copy from https://github.com/netty/netty/wiki/User-guide-for-4.x
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new HttpRequestDecoder(), new HttpServerRequestHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(port).sync();
// Wait until the server socket is closed.
// In this example, this does not happen, but you can do that to gracefully
// shut down your server.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
Run Code Online (Sandbox Code Playgroud)
public class HttpServerRequestHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// step 1 get data from browser
if (msg instanceof LastHttpContent) {
ctx.close();
return;
}
DefaultHttpRequest httpMessage = (DefaultHttpRequest) msg;
System.out.println("?????====================");
System.out.println(msg);
System.out.println();
doWork(ctx, httpMessage);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
private void doWork(ChannelHandlerContext ctx, final DefaultHttpRequest msg) {
// step 2 send data to website
// translate url into host and port
String host = msg.uri();
int port = 80;
if (host.startsWith("https://")) {
host = host.replaceFirst("https://", "");
port = 443;
} else if (host.startsWith("http://")) {
host = host.replaceFirst("http://", "");
port = 80;
}
if (host.contains(":443")) {
host = host.replace(":443", "");
port = 443;
}
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
//b.option(ChannelOption.AUTO_READ, true);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new HttpServerResponseHandler(msg), new HttpRequestEncoder());
}
});
// question 1
ChannelFuture f = b.connect(host, port).sync();
//ChannelFuture f = b.connect("www.baidu.com", 443).sync();
f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
}
}
}
Run Code Online (Sandbox Code Playgroud)
public class HttpServerResponseHandler extends ChannelOutboundHandlerAdapter {
private Object httpMessage;
public HttpServerResponseHandler(Object o) {
this.httpMessage = o;
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
System.out.println("??????=========================");
System.out.println(httpMessage);
System.out.println();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
cause.printStackTrace();
ctx.close();
}
@Override
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
SocketAddress localAddress, ChannelPromise promise) throws Exception {
System.out.println("connect !!!!!!!!!!!");
// question 2
ctx.writeAndFlush(httpMessage);
}
}
Run Code Online (Sandbox Code Playgroud)
很巧的是,出于学习目的,我也一直在做Netty代理服务器。我有一个完整的工作代码,您可以在我的GitHub上找到,但我将在这里回答您的问题。Netty 也有一个官方代理服务器示例,但与我的代码不同,它们没有单元测试。
(仅供参考,我的代码是用 Kotlin 编写的)。
核心理念:
创建代理服务器时,您需要一个服务器来接受客户端请求,以及一个用于远程代理的客户端。您创建了服务器,但没有创建客户端。最好重用EventLoop服务器创建的内容,而不是为客户端创建新的内容。每个事件循环都在专用线程上运行,因此创建更多事件循环会产生额外的线程,在接受者Channel和客户端之间交换数据时需要上下文切换Channel。
如何将url转换为主机和端口
为了简单起见,我使用了HttpObjectAggregator将 anHttpMessage及其后续内容聚合HttpContents为单个FullHttpRequestor FullHttpResponse(取决于它是否用于处理请求或响应)。设置 URL 很简单:只需调用FullHttpRequest.setUri即可。
要获取主机和端口,请在客户端通道上调用Channel.remoteAddress()并将结果转换SocketAddress为InetSocketAddress,从中您可以获取主机和端口。不要忘记类似地重置Host标题(如果存在)。
我怎样才能得到响应数据
建立客户端通道(您缺少该通道)后,您需要在该通道上发出请求。客户端通道有一个处理程序,其中包含对原始服务器通道的引用。一旦处理程序收到响应,它将其写入服务器通道。
| 归档时间: |
|
| 查看次数: |
7035 次 |
| 最近记录: |