工作上有转发程序,从一个客户端发送数据到转发程序再发送到另一个服务端。整个传输协议是基于交通部809协议要求的。
809协议文档:()
目录
问题1:怎么关闭链路
问题2:当链路无故关闭,怎么重连
问题3:关于主从链路之间的链路保持请求
遇到的主要问题如下:
问题1:怎么关闭链路
netty中有两个关于通道的关闭方法,一个是断开连接 channel.disconnect() 一个是关闭通道 channel.close()
根据网上资料 close()方法中会先去检测链路是否畅通,然后执行channeldisconnect方法 最后统一执行channelClosed的方法
(可以看看这个大佬写的:)
现在有个问题环境:两个客户端同时连到我的服务端,并且同时发送相同的一条809协议的1001主链路请求数据,我只需要一个客户端连我,并且是后面连接到我客户端进行一个保存,然后将前面的客户端通道给断掉。
解决方法如下:
在start启动类中创建了一个key 为ip channel 为value 的map 然后通过每次客户端去连接服务端时,将当前channel记录,如果ip相同,就把上一个ip的channel给断掉
如下进行实现的:
一个Start类,主要是记录一些成员变量和常量, 这里记录了address(ip+port),ip地址, 一个存放channel的map,放在这里是为了防止每次有客户端连接服务器,会new一个handler的问题,放在handler中的话就不能记录每次来的ip和channel。所以放在这个类里面。
public class Start{
// 存放ip地址和channel
public static Map<String, Channel> checkMap = new HashMap<>();
public static void initServerChannel(){}
public static void initClientChannel(){}
public static void main(){}
......
}
然后在serverHandler中需要去创建一个方法,去判断需要怎么去断开连接。写了如下一个方法,就是来一条连接将其存到map,
如果有新的客户端连接,那么就需要把当前的channel 也就是存储在map里的channel关掉,然后将新的channel存入到map当中
void tocheckActive(Channel channel) {
String socket = channel.remoteAddress().toString().trim();
System.out.println("当前Address地址为: " + socket);
int index = socket.indexOf(":");
String sockets = socket.substring(1, index).trim();
System.out.println("当前Ip地址为:" + sockets);
if (Start.checkMap.size()==0) {
Start.checkMap.put(sockets, channel);
System.out.println("当前map为空,存入channel");
} else {
if (Start.checkMap.containsKey(sockets)) {
// 匹配上了。断掉map中的channel,再将其新的channel放入
System.out.println("新连的链路与当前链路是一个ip,断掉当前链路...");
Start.checkMap.get(sockets).disconnect();
Start.checkMap.get(sockets).close();
try {
Thread.sleep(1500L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("存入新链路...");
Start.checkMap.put(sockets, channel);
}
}
在serverHandler中使用这个方法,再执行channelActive方法后的内容
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
tocheckActive(ctx.channel());
System.out.println("准备连接");
Connection.checkMap.clear();
}
例子:
启动服务器,先启动一个客户端连接
然后在去启动第二个客户端,会将第一个客户端关闭,然后第二个连接
这个就是去保证如果两个客户端一次性连接 而服务器只需要最新的那个客户端的连接链路的方法
问题2:当链路无故关闭,怎么重连
问题环境:如果连接的客户端断开了,那么按照正常情况需要重新连接
我这个处理的流程是
1.channelInactive方法是针对链路断开后走的方法,这个时候需要将链路去断开,使用disconnect方法去让链路取消连接
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("当前链路断开..");
//System.out.println("ctx.disconnect:"+ctx.disconnect());
ChannelFuture future = ctx.disconnect();
System.out.println("future:"+future);
if(future.isSuccess()){
ctx.channel = null;
reconn();
}
}
如果断开是成功的,那么去走客户端重连方法,这个时候需要注意的是基于809协议的重连方法,需要在链路重新建立起连接后发送1001的链路请求连接的数据,
所以我的重连方法是如下:
public static void reconn() {
try {
bootStrap.connect(Address,Port).addListener(
new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
System.out.println("连击失败了");
final EventLoop loop = future.channel().eventLoop();
System.out.println("等待链接");
loop.schedule(new Runnable() {
@Override
public void run() {
System.out.println("开始重连");
reconn();
}
}, 3L, TimeUnit.SECONDS);
} else {
SXclientChannel = future.channel();
System.out.println("链接成功...");
Thread.sleep(1000L);
System.out.println("1001数据再次发送");
future.channel.writeAndFlush(Input1001).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future)throws Exception {
if (!future.isSuccess()) {
System.out.println("1001数据发送失败");
}
}
});
}
}
});
} catch (Exception e) {
e.printStackTrace();
System.out.println("连接出现错误" + e.getMessage());
// future.channel().closeFuture().sync();
}
}
就是先用connect方法去连接,启动一个监听,如果成功了就用这条链路发送数据 如果失败了,就是用Eventloop去循环等待执行recon方法,直到连接成功。(有点递归的味道)
就像如下:
当前模拟的服务端是被连接了。
然后连接成功的数据发送1001数据
如果这个时候我把主链路的7100端口断掉,那么他会重连并且发送1001数据,如果服务器异常关闭很久,主链路应该一直等待连接
我将监听停掉,模拟服务出现故障一直不能连接,现在就是等待连接的状态
然后我打开监听,这样就能够又连上了
问题3:关于主从链路之间的链路保持请求
这个是netty心跳加上809协议要求链路畅通
netty有一个长连接,在初始化通道的时候需要打开 keepAlive 。然后定时发送心跳,让链路畅通。结合890协议需要主链路来回传递1005、1006数据。从链路来回传递9005 和9006数据
boootStrap.group().option(ChannelOption.SO_KEEPALIVE, true)
数据传输按一定时间就要发送链路保持畅通数据