Practical Netty (5) TCP反向代理服务器
以下针对 TCP 反向代理服务器。
1. 前端连接被创建时,创建后端连接
一个平凡的 ServerBootstrap 会有如下的一个语句:
serverBootstrap.setPipelineFactory(
new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline p = pipeline();
p.addLast("handler", new PoechantProxyHandler());
return p;
}
});
这个PoechantProxyHandler
如何实现,就成了关键了。
每个 connection 建立后,会创建一个 channel 专门用于服务这个连接。此时会响应ChannelPipelineHandler
的此方法:
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
这时你可以为这个 connection(不妨称其为前端连接),创建一个与后端连接的 connection(不妨称其为后端连接)。此时对于后端服务器,你要扮演的是 client 的角色,所以需要一个 ClientBootstrap。该 client 连接成功后,就可以从前端连接中读取数据了。
private volatile Channel outboundChannel;
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
final Channel inboundChannel = e.getChannel();
inboundChannel.setReadable(false);
ClientBootstrap cb = new ClientBootstrap(cf);
cb.getPipeline().addLast("handler", new BackendChannelHandler(e.getChannel()));
ChannelFuture f = cb.connect(new InetSocketAddress(remoteHost, remotePort));
outboundChannel = f.getChannel();
f.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
inboundChannel.setReadable(true);
} else {
inboundChannel.close();
}
}
});
}
ClientBootstrap.connect
后会创建一个 Channel,与后端服务器连接。对于 ClientBootstrap 是不存在 parent channel 和 child channel 这样需要考虑的策略的。
转载请注明来自柳大的CSDN博客:Blog.CSDN.net/Poechant,微博:weibo.com/lauginhom
2. 前端接收的消息,转发给后端
亲,请边看下面的代码,边读我写的这段话。msg
是从前端Channel
(因为这是一个为前端ServerBootstrap
服务的 ChannelPipelineHandler)拿到的消息,然后把它写入到后端连接的Channel
(outboundChannel
就是前面我们在前端连接被建立时创建的后端Channel
)。
@Override
public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e)
throws Exception {
ChannelBuffer msg = (ChannelBuffer) e.getMessage();
synchronized (trafficLock) {
outboundChannel.write(msg);
if (!outboundChannel.isWritable()) {
e.getChannel().setReadable(false);
}
}
}
2.1. 对后端通道的操作,要做同步
需要注意的是,凡是对 outboundHandler 的操作都是需要同步的,因为 PoechantProxyHandler 中的 outboundHandler 是 volatile,就是异变的,为什么呢?因为它的OP_READ
(相关的讨论可以参见这里)可能被改变,所以要同步。所以这里就用到了 java 的 synchronized 同步代码块,在每个 ProchantProxyHandler 的实例身上都有一个trafficLock
成员,当做锁来使用,这是一个 Object,如下:
final Object trafficLock = new Object();
2.2. 后端不可写,则前端不要读
当然,说的就是这段:
if (!outboundChannel.isWritable()) {
e.getChannel().setReadable(false);
}
转载请注明来自柳大的CSDN博客:Blog.CSDN.net/Poechant,微博:weibo.com/lauginhom
3. 连接可操作状态改变时触发 channelInterestChanged
public void channelInterestChanged(ChannelHandlerContext ctx,
ChannelStateEvent e)
throws Exception
简单说,就是“This function was invoked when aChannel
'sinterestOps
was changed.” 那什么是interestOps
呢?
The interestOps value which tells that only read operation has been suspended.
org.jboss.netty.channel.Channel.OP_NONE
org.jboss.netty.channel.Channel.OP_READ
org.jboss.netty.channel.Channel.OP_WRITE
org.jboss.netty.channel.Channel.OP_READ_WRITE
他们的取值范围如下:
Modifier and Type
Constant Field
Value
public static final int |
OP_NONE |
0 = 0000 |
public static final int |
OP_READ |
1 = 0001 |
public static final int |
OP_WRITE |
4 = 0100 |
public static final int |
OP_READ_WRITE |
5 = 0101 |
前端连接可写时,后端连接就需要可读。
@Override
public void channelInterestChanged(ChannelHandlerContext ctx,
ChannelStateEvent e) throws Exception {
synchronized (trafficLock) {
if (e.getChannel().isWritable()) {
if (outboundChannel != null) {
outboundChannel.setReadable(true);
}
}
}
}
转载请注明来自柳大的CSDN博客:Blog.CSDN.net/Poechant,微博:weibo.com/lauginhom
4. 后端连接的 ChannelPipelineHandler
后端连接有相应的 Handler,在创建后端连接时:
ClientBootstrap cb = new ClientBootstrap(cf);
cb.getPipeline().addLast("handler", new OutboundHandler(e.getChannel()));
所以可如下定义:
private class OutboundHandler extends SimpleChannelUpstreamHandler {
private final Channel inboundChannel;
OutboundHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
}
public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e)
throws Exception {…}
public void channelInterestChanged(ChannelHandlerContext ctx,
ChannelStateEvent e) throws Exception {…}
}
其中 messageReceived 和 channelInterestChanged 的实现方式如下:
@Override
public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e)
throws Exception {
ChannelBuffer msg = (ChannelBuffer) e.getMessage();
synchronized (trafficLock) {
inboundChannel.write(msg);
if (!inboundChannel.isWritable()) {
e.getChannel().setReadable(false);
}
}
}
@Override
public void channelInterestChanged(ChannelHandlerContext ctx,
ChannelStateEvent e) throws Exception {
synchronized (trafficLock) {
if (e.getChannel().isWritable()) {
inboundChannel.setReadable(true);
}
}
}
这里可以概括一下。
if
and if
then
frontendchannelOpen
|
N/A |
set frontend writable |
frontendmessageReceived
|
backend nonwritable |
set frontend nonreadable |
frontendchannelInterestChanged
|
frontend writable |
set backend readable |
backendmessageReceived
|
frontend nonwritable |
set backend nonreadable |
backendchannelInterestChanged
|
backend writable |
set frontend readable |
可以看到:
- 当一方收到消息时,关心的是另一方可不可写,如果另一方不可写则设置该方不可读(因为收到消息,自己一定是可读的,而对方的读状态不需考虑);
- 当一方
interestOps
改变时,关心的是自己是否变成可写,如果自己可写则设置对方可读(因为自己的读状态,只需要到messageReceived
时考虑即可,而对于自己变成不可写这种情况,在messageReceived
中已经考虑了)。
-
转载请注明来自柳大的CSDN博客:Blog.CSDN.net/Poechant,微博:weibo.com/lauginhom
-
分享到:
相关推荐
基于Netty实现的内网穿透&反向代理的工具 (支持TCP上层协议和HTTP的穿透式反向代理).zip
netty搭建tcp自定义协议websocket服务器, 支持ssl demo. 适用于netty初学者, netty搭建自定义协议, websocket服务器, 聊天室, 一个端口集成多协议,包括ssl协议
Java异步NIO框架Netty实现高性能高并发,通过netty搭建TCP、UDP服务,支持物联网设备上行,下行
reactor-netty, TCP/HTTP/UDP 客户机/服务器,带有联网的反应器 反应器联网 http://projectreactor.io/docs/netty/release/api/在软件许可证 2.0许可,,,。
利用netty实现Modbus TCP client/server READ COILS | 0x01 READ DISCRETE INPUTS | 0x02 READ HOLDING REGISTERS | 0x03 READ INPUT REGISTERS | 0x04 WRITE SINGLE COIL | 0x05 WRITE SINGLE REGISTER | 0x06 ...
使用SpringBoot2.x集成Netty4.x创建基于TCP/IP协议的服务端和客户端的Demo代码案例 使用了SpringBoot+swaggerUI方式进行测试,客户端可以给服务端发送消息,服务端也可以给已经连上的客户端发送消息,使用了通道保活...
主要介绍了使用Netty解决TCP粘包和拆包问题过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
Netty实现TCP服务
Netty反向代理 使用 Netty 传输编写的反向代理 要运行该程序,请在位于 src/main/resources 目录内的 config.properties 文件中填写必要的属性,然后使用您的 IDE 运行 HexDumpProxy 类。 特性描述如下。 ...
基于tcp通讯,涉及java的netty服务器的推送功能和c++socket的封装以及protobuf在java和c++中的使用。
springboot集成netty实现代理服务器,实现http和https请求的代理功能
JAVA采用Netty库实现基于以DTU传输的TCP服务器 ,可以支持多端口通讯 ,同时也支持 多协议解析
netty搭建tcp服务,并以相应的编码解决粘包,拆包问题
使用netty实现TCP长链接消息写入kafka以及kafka批量消费数据,数据可以批量进行操作
客户端发送16进制给服务端,并行实现socket通道活动状态...netty作为服务器端如何主动的向传感器发送消息,我尝试当每个传感器连接到netty(TCP/IP)时使用一个map把该channelSocket的id和该channelSocket绑定在一起。
基于netty的TCP / Http请求转发代理程序 简介 在特定的网络环境或安全审核要求下,我们可能会面临网络被限定为单向访问的情况,本工具可以实现在单向网络中设置代理从而实现双向访问的目的。透工具。 依赖/知识准备 ...
netty开发的http代理服务器,含三个实例,都可正常运行
使用Thymeleaf模板引擎,Netty。实现硬件通过TCP连接Netty服务器,并将运行数据显示在网页端,实现信息交互。
反向代理 通过Netty基于Java NIO的https代理服务器的实现。 这是一个简单的工作原理: 要通过浏览器对其进行测试,我们将需要设置一个虚拟主机名... 为了向后端的Web服务器应用程序提供反向代理,我创建了一个Flask