netty中代理通信的编程模型。
代理就是通过一个中间服务器去目标服务器请求所要的信息,然后通过代理服务器返回信息给客户端。
在netty中,netty客户端请求代理服务器,连接到代理服务器时->inbound,代理服务器马上去连接目标服务器->outbound,当连接目标服务器成功时,这是开始读取客户端给代理服务器的数据,然后使用outbound写入目标服务器,目标服务器返回数据,代理服务器读取,并使用inbound把数据返回客户端。
客户端程序
package proxy4;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
public class Client {
public void connect(String host, int port) throws Exception {
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new ObjectEncoder(),
new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
new ClientHandler()
);
}
});
// Start the client.
ChannelFuture f = b.connect(host, port).sync();
// Wait until the connection is closed.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new Client().connect("127.0.0.1", 12359);
}
}
package proxy4;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class ClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client to the proxy server, said: I was a client, give my regards to the target server!");
ChannelFuture f = ctx.writeAndFlush("I'm client, give my regards to the target server!");
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
System.out.println("===========write message success==========");
}
});
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String mess = (String) msg;
System.out.println("Receiving a message from the proxy server to:" + mess);
ctx.close();
}
}
目标服务器端程序
package proxy4;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
public class Target {
private String targetHost;
private int targetPort;
public Target(String targetHost, int targetPort) {
this.targetHost = targetHost;
this.targetPort = targetPort;
}
public void run() throws Exception {
System.err.println("Target host:" + targetHost + " targetPort:" + targetPort);
// Configure the bootstrap.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
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 {
// 注册handler
ch.pipeline().addLast(
new ObjectEncoder(),
new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
new TargetHandler()
);
}
})
.bind(targetPort).sync().channel().closeFuture().sync();
//监听本地的一个端口,当有客户端请求时,然后向目标服务器发送请求,获取消息,然后发送给客户端
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new Target("127.0.0.1", 12358).run();
}
}
package proxy4;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class TargetHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println(">>>>>>>>ACTIVE>>>>>>>>");
}
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("proxy said:" + msg.toString());
// 向代理服务器发送消息
System.out.println("After the target server to the proxy server receives a message to the proxy server, said: I am the target server, I thank you on behalf of the client.");
String response = "I was the target server, I thank you for the client.";
ctx.write(response);
ctx.flush();
ctx.close();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println(">>>>>>>>IN-ACTIVE>>>>>>>>");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
/**
* Closes the specified channel after all queued write requests are flushed.
*/
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}
代理服务器端程序
package proxy4;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
public class Proxy {
private final int localPort;
private final String remoteHost;
private final int remotePort;
/**
* @param localPort
* @param remoteHost
* @param remotePort
*/
public Proxy(int localPort, String remoteHost, int remotePort) {
this.localPort = localPort;
this.remoteHost = remoteHost;
this.remotePort = remotePort;
}
public void run() throws Exception {
System.err.println(
"Proxying *:" + localPort + " to " +
remoteHost + ':' + remotePort + " ...");
// Configure the bootstrap.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
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 {
// 注册handler
ch.pipeline().addLast(
new ObjectEncoder(),
new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
new ProxyFrontendHandler(remoteHost, remotePort)
);
}
}).childOption(ChannelOption.AUTO_READ, false);
ChannelFuture f = b.bind(localPort).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new Proxy(12359, "127.0.0.1", 12358).run();
}
}
package proxy4;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
public class ProxyFrontendHandler extends ChannelInboundHandlerAdapter {
private final String remoteHost;
private final int remotePort;
//代理服务器和目标服务器之间的通道(从代理服务器出去所以是outbound过境)
private volatile Channel outboundChannel;
/**
* remoteHost和remotePort表示目标服务器
*
* @param remoteHost
* @param remotePort
*/
public ProxyFrontendHandler(String remoteHost, int remotePort) {
this.remoteHost = remoteHost;
this.remotePort = remotePort;
}
/**
* 当客户端和代理服务器建立通道连接时,调用此方法
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
/**
* 客户端和代理服务器的连接通道
* 入境的通道
*/
final Channel inboundChannel = ctx.channel();
// Start the connection attempt.
Bootstrap b = new Bootstrap();
b.group(inboundChannel.eventLoop())
.channel(ctx.channel().getClass())
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new ObjectEncoder(),
new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
new ProxyBackendHandler(inboundChannel)
);
}
});
/**
* 连接目标服务器
* ChannelFuture
* Netty中的IO操作是异步的,
* 包括bind、write、connect等操作会简单的返回一个ChannelFuture,调用者并不能立刻获得结果。
* 当future对象刚刚创建时,处于非完成状态。可以通过isDone()方法来判断当前操作是否完成。通过isSuccess()判断已完成的当前操作是否成功,getCause()来获取已完成的当前操作失败的原因,isCancelled()来判断已完成的当前操作是否被取消。
* 调用者可以通过返回的ChannelFuture来获取操作执行的状态,注册监听函数来执行完成后的操作。
*/
ChannelFuture f = b.connect(remoteHost, remotePort);
/**
* 获得代理服务器和目标服务器之间的连接通道
*/
outboundChannel = f.channel();
/**
* ChannelFutureListener
* 监听ChannelFuture的状态
* 注册监听函数来执行完成后的操作
*/
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
// connection complete start to read first data
inboundChannel.read();
} else {
// Close the connection if the connection attempt has failed.
inboundChannel.close();
}
}
});
}
/**
* 在这里接收客户端的消息
* 在客户端和代理服务器建立连接时,也获得了代理服务器和目标服务器的通道outbound,
* 通过outbound写入消息到目标服务器
*
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("from client message:" + msg.toString());
System.out.println("After receiving the proxy server to the client's message to the target server, said: I am a proxy server, a client asked me to say hello to you.");
String proxyToServer = "I am a proxy server, the client asked me to say hello to you.";
if (outboundChannel.isActive()) {
outboundChannel.writeAndFlush(proxyToServer).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
// was able to flush out data, start to read the next chunk
ctx.channel().read();
} else {
future.channel().close();
}
}
});
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (outboundChannel != null) {
closeOnFlush(outboundChannel);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
/**
* Closes the specified channel after all queued write requests are flushed.
*/
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}
package proxy4;
import io.netty.channel.*;
public class ProxyBackendHandler extends ChannelInboundHandlerAdapter {
private final Channel inboundChannel;
public ProxyBackendHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
}
//当和目标服务器的通道连接建立时
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println(">>>>>>>>>>ACTIVE>>>>>>>>>>");
}
/**
* msg是从目标服务器返回的消息
*
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("Target server returns data:" + msg.toString());
/**
* 接收目标服务器发送来的数据并打印
* 然后把数据写入代理服务器和客户端的通道里
*/
//通过inboundChannel向客户端写入数据
System.out.println("After the proxy server receives a response to the target server to the client to say: I am a proxy server, the target server to replace him, I say to you, thank you. ");
String resDataToClient = "I am a proxy server, the target server to replace him, I say to you, thank you.";
inboundChannel.writeAndFlush(resDataToClient).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
inboundChannel.close();
} else {
future.channel().close();
}
}
});
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println(">>>>>>>>>>IN-ACTIVE>>>>>>>>>>");
ProxyFrontendHandler.closeOnFlush(inboundChannel);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ProxyFrontendHandler.closeOnFlush(ctx.channel());
}
}
运行结果:
目标服务器
Target host:127.0.0.1 targetPort:12358
log4j:WARN No appenders could be found for logger (io.netty.util.internal.logging.InternalLoggerFactory).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
>>>>>>>>ACTIVE>>>>>>>>
proxy said:I am a proxy server, the client asked me to say hello to you.
After the target server to the proxy server receives a message to the proxy server, said: I am the target server, I thank you on behalf of the client.
>>>>>>>>IN-ACTIVE>>>>>>>>
>>>>>>>>ACTIVE>>>>>>>>
proxy said:I am a proxy server, the client asked me to say hello to you.
After the target server to the proxy server receives a message to the proxy server, said: I am the target server, I thank you on behalf of the client.
>>>>>>>>IN-ACTIVE>>>>>>>>
>>>>>>>>ACTIVE>>>>>>>>
proxy said:I am a proxy server, the client asked me to say hello to you.
After the target server to the proxy server receives a message to the proxy server, said: I am the target server, I thank you on behalf of the client.
>>>>>>>>IN-ACTIVE>>>>>>>>
>>>>>>>>ACTIVE>>>>>>>>
proxy said:I am a proxy server, the client asked me to say hello to you.
After the target server to the proxy server receives a message to the proxy server, said: I am the target server, I thank you on behalf of the client.
>>>>>>>>IN-ACTIVE>>>>>>>>
>>>>>>>>ACTIVE>>>>>>>>
proxy said:I am a proxy server, the client asked me to say hello to you.
After the target server to the proxy server receives a message to the proxy server, said: I am the target server, I thank you on behalf of the client.
>>>>>>>>IN-ACTIVE>>>>>>>>
代理服务器
Proxying *:12359 to 127.0.0.1:12358 ...
log4j:WARN No appenders could be found for logger (io.netty.util.internal.logging.InternalLoggerFactory).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
>>>>>>>>>>ACTIVE>>>>>>>>>>
from client message:I'm client, give my regards to the target server!
After receiving the proxy server to the client's message to the target server, said: I am a proxy server, a client asked me to say hello to you.
Target server returns data:I was the target server, I thank you for the client.
After the proxy server receives a response to the target server to the client to say: I am a proxy server, the target server to replace him, I say to you, thank you.
>>>>>>>>>>IN-ACTIVE>>>>>>>>>>
客户端
Client to the proxy server, said: I was a client, give my regards to the target server!
===========write message success==========
Receiving a message from the proxy server to:I am a proxy server, the target server to replace him, I say to you, thank you.
Process finished with exit code 0
==========END==========