netty4 断线重连
转载:http://www.tuicool.com/articles/B7RzMbY
一 实现心跳检测
原理:当服务端每隔一段时间就会向客户端发送心跳包,客户端收到心跳包后同样也会回一个心跳包给服务端
一般情况下,客户端与服务端在指定时间内没有任何读写请求,就会认为连接是idle(空闲的)的。此时,客户端需要向服务端发送心跳消息,来维持服务端与客户端的链接。那么怎么判断客户端在指定时间里没有任何读写请求呢?netty中为我们提供一个特别好用的IdleStateHandler来干这个苦差事!
在服务端工作线程中添加:arg0.pipeline().addLast("ping", new IdleStateHandler(25, 15, 10,TimeUnit.SECONDS));
这个处理器,它的作用就是用来检测客户端的读取超时的,该类的第一个参数是指定读操作空闲秒数,第二个参数是指定写操作的空闲秒数,第三个参数是指定读写空闲秒数,
当有操作操作超出指定空闲秒数时,便会触发UserEventTriggered事件。所以我们只需要在自己的handler中截获该事件,然后发起相应的操作即可(比如说发起心跳操作)。
以下是我们自定义的handler中的代码:
/**
* 一段时间未进行读写操作 回调
*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
throws Exception {
// TODO Auto-generated method stub
super.userEventTriggered(ctx, evt); if (evt instanceof IdleStateEvent) { IdleStateEvent event = (IdleStateEvent) evt; if (event.state().equals(IdleState.READER_IDLE)) {
//未进行读操作
System.out.println("READER_IDLE");
// 超时关闭channel
ctx.close(); } else if (event.state().equals(IdleState.WRITER_IDLE)) { } else if (event.state().equals(IdleState.ALL_IDLE)) {
//未进行读写
System.out.println("ALL_IDLE");
// 发送心跳消息
MsgHandleService.getInstance().sendMsgUtil.sendHeartMessage(ctx); } }
} 也就是说 服务端在10s内未进行读写操作,就会向客户端发送心跳包,客户端收到心跳包后立即回复心跳包给服务端,此时服务端就进行了读操作,
也就不会触发IdleState.READER_IDLE(未读操作状态),若客户端异常掉线了,并不能响应服务端发来的心跳包,在25s后就会触发IdleState.READER_IDLE(未读操作状态),
此时服务器就会将通道关闭。
二 客户端实现断线重连
原理当客户端连接服务器时
bootstrap.connect(new InetSocketAddress(
serverIP, port)); 会返回一个ChannelFuture的对象,我们对这个对象进行监听。 代码如下:
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.Log; import com.ld.qmwj.Config;
import com.ld.qmwj.MyApplication; import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit; import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder; /**
* Created by zsg on 2015/11/21.
*/
public class MyClient implements Config {
private static Bootstrap bootstrap;
private static ChannelFutureListener channelFutureListener = null; public MyClient() { } // 初始化客户端
public static void initClient() { NioEventLoopGroup group = new NioEventLoopGroup(); // Client服务启动器 3.x的ClientBootstrap
// 改为Bootstrap,且构造函数变化很大,这里用无参构造。
bootstrap = new Bootstrap();
// 指定EventLoopGroup
bootstrap.group(group);
// 指定channel类型
bootstrap.channel(NioSocketChannel.class);
// 指定Handler
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 创建分隔符缓冲对象
ByteBuf delimiter = Unpooled.copiedBuffer("#"
.getBytes());
// 当达到最大长度仍没找到分隔符 就抛出异常
ch.pipeline().addLast(
new DelimiterBasedFrameDecoder(10000, true, false, delimiter));
// 将消息转化成字符串对象 下面的到的消息就不用转化了
//解码
ch.pipeline().addLast(new StringEncoder(Charset.forName("UTF-8")));
ch.pipeline().addLast(new StringDecoder(Charset.forName("GBK")));
ch.pipeline().addLast(new MyClientHandler());
}
});
//设置TCP协议的属性
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
bootstrap.option(ChannelOption.TCP_NODELAY, true);
bootstrap.option(ChannelOption.SO_TIMEOUT, 5000); channelFutureListener = new ChannelFutureListener() {
public void operationComplete(ChannelFuture f) throws Exception {
// Log.d(Config.TAG, "isDone:" + f.isDone() + " isSuccess:" + f.isSuccess() +
// " cause" + f.cause() + " isCancelled" + f.isCancelled()); if (f.isSuccess()) {
Log.d(Config.TAG, "重新连接服务器成功"); } else {
Log.d(Config.TAG, "重新连接服务器失败");
// 3秒后重新连接
f.channel().eventLoop().schedule(new Runnable() {
@Override
public void run() {
doConnect();
}
}, 3, TimeUnit.SECONDS);
}
}
}; } // 连接到服务端
public static void doConnect() {
Log.d(TAG, "doConnect");
ChannelFuture future = null;
try {
future = bootstrap.connect(new InetSocketAddress(
serverIP, port));
future.addListener(channelFutureListener); } catch (Exception e) {
e.printStackTrace();
//future.addListener(channelFutureListener);
Log.d(TAG, "关闭连接");
} } }
监听到连接服务器失败时,会在3秒后重新连接(执行doConnect方法)
这还不够,当客户端掉线时要进行重新连接
在我们自己定义逻辑处理的Handler中
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Log.d(Config.TAG, "与服务器断开连接服务器");
super.channelInactive(ctx);
MsgHandle.getInstance().channel = null; //重新连接服务器
ctx.channel().eventLoop().schedule(new Runnable() {
@Override
public void run() {
MyClient.doConnect();
}
}, 2, TimeUnit.SECONDS);
ctx.close();
}
netty4 断线重连的更多相关文章
- 关于socket tcp 断线重连
这个问题困扰过我几次,都没有来得及研究,今天研究一下. 首先写一个最简易的socket tcp程序,连接成功后再关闭服务器然后再用客户端各种操作看是什么情况 测试表明 (1)客户端已经连接,当服务端关 ...
- Netty 客户端断线重连
client 关闭后会执行 finally 代码块,可以在这里可以进行重连操作 public class NettyClient implements Runnable { private final ...
- ActiveMQ的断线重连机制
断线重连机制是ActiveMQ的高可用性具体体现之一.ActiveMQ提供failover机制去实现断线重连的高可用性,可以使得连接断开之后,不断的重试连接到一个或多个brokerURL. 默认情况下 ...
- ADOConnection断线重连
问题: ADOConnection断线重连问题描述: 使用ADOConnection连接oracle数据库,开始正常,当网络断开时数据库连接失败(此时查询ADOConnection.connected ...
- 发现电脑上装着liteide,就用golang做一个TCP通讯测试(支持先启动client端和断线重连)
1.参考https://www.cnblogs.com/yin5th/p/9274495.html server端 main.go package main import ( "fmt&qu ...
- Netty断线重连
Netty断线重连 最近使用Netty开发一个中转服务,需要一直保持与Server端的连接,网络中断后需要可以自动重连,查询官网资料,实现方案很简单,核心思想是在channelUnregistered ...
- 浅谈IM软件client的断线重连、心跳和长在线
版权声明:原创文章,未经博主同意禁止转载.欢迎点击头像上方"郭晓东的专栏"查看专栏 https://blog.csdn.net/hherima/article/details/27 ...
- socket 断线重连
send发送数据时,发送失败,进行如下重连处理: ) < )//serbuf中有数据可以发送才会执行这条语句 { printf("serial to tcp send msg erro ...
- android 实现mqtt消息推送,以及不停断线重连的问题解决
前段时间项目用到mqtt的消息推送,整理一下代码,代码的原型是网上找的,具体哪个地址已经忘记了. 代码的实现是新建了一个MyMqttService,全部功能都在里面实现,包括连服务器,断线重连,订阅消 ...
随机推荐
- Object-c:两种文件读写的对比
一.读写方法对比:(主要针对本地读取本地文件) 方式\操作 读 写 非URL方式 stringWithContentsOfFile writeToFile URL方式 stringWithConten ...
- POJ 1321棋盘问题
题意: 给定一个n*n(n<=8)的棋盘, 有些地方能放棋子, 有些不能. 放m个棋子, 求能使这m个棋子不同行且不同列的方案数. 分析: 用一个一维数组标记行和列, 深搜一下. #includ ...
- oracle的基本查询~下
SQL> --别名SQL> select ename 姓名, job as "工作" ,sal "薪水" from emp; 姓名 ...
- iOS - UIRefreshControl 刷新数据
前言 NS_CLASS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED @interface UIRefreshControl : UIControl 1.UIRefresh ...
- iOS - KVC 键值编码
1.KVC KVC 是 Key-Value Coding 的简写,是键值编码的意思,属于 runtime 方法.Key Value Coding 是 cocoa 的一个标准组成部分,是间接给对象属性设 ...
- redis问题
phalcon 在读取数据的时候如果说reids数据库没有连接成功的话,则会显示一下错误:
- random使用
package com.cz.test; import java.util.Random; public class RandomTest { public static void main(Stri ...
- HTTP refere
什么是 http refere ? 简言之,放在web浏览器的header上的 当我通过我的主页访问我朋友的网站时,就可以获得refere , 可以获取网页访问流量,也可以防盗链 一般 referer ...
- poj2074Line of Sight(直线相交)
链接 几何细节题. 对于每一个障碍物可以求出它在地产线上的覆盖区间,如下图. 紫色部分即为每个障碍物所覆盖掉的区间,求出所有的,扫描一遍即可. 几个需要注意的地方:直线可能与地产线没有交点,可视区间可 ...
- (五)stm32工程代码HardFault异常查错调试方法
一.导致异常的原因很多,例如:直接使用未分配空间的指针.栈溢出等一场非法操作便会使程序进入HardFault异常状态.下面介绍怎么找出程序中的异常. 接下来在keil_MDK工程中,编译代码,并deb ...