转载: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 断线重连的更多相关文章

  1. 关于socket tcp 断线重连

    这个问题困扰过我几次,都没有来得及研究,今天研究一下. 首先写一个最简易的socket tcp程序,连接成功后再关闭服务器然后再用客户端各种操作看是什么情况 测试表明 (1)客户端已经连接,当服务端关 ...

  2. Netty 客户端断线重连

    client 关闭后会执行 finally 代码块,可以在这里可以进行重连操作 public class NettyClient implements Runnable { private final ...

  3. ActiveMQ的断线重连机制

    断线重连机制是ActiveMQ的高可用性具体体现之一.ActiveMQ提供failover机制去实现断线重连的高可用性,可以使得连接断开之后,不断的重试连接到一个或多个brokerURL. 默认情况下 ...

  4. ADOConnection断线重连

    问题: ADOConnection断线重连问题描述: 使用ADOConnection连接oracle数据库,开始正常,当网络断开时数据库连接失败(此时查询ADOConnection.connected ...

  5. 发现电脑上装着liteide,就用golang做一个TCP通讯测试(支持先启动client端和断线重连)

    1.参考https://www.cnblogs.com/yin5th/p/9274495.html server端 main.go package main import ( "fmt&qu ...

  6. Netty断线重连

    Netty断线重连 最近使用Netty开发一个中转服务,需要一直保持与Server端的连接,网络中断后需要可以自动重连,查询官网资料,实现方案很简单,核心思想是在channelUnregistered ...

  7. 浅谈IM软件client的断线重连、心跳和长在线

    版权声明:原创文章,未经博主同意禁止转载.欢迎点击头像上方"郭晓东的专栏"查看专栏 https://blog.csdn.net/hherima/article/details/27 ...

  8. socket 断线重连

    send发送数据时,发送失败,进行如下重连处理: ) < )//serbuf中有数据可以发送才会执行这条语句 { printf("serial to tcp send msg erro ...

  9. android 实现mqtt消息推送,以及不停断线重连的问题解决

    前段时间项目用到mqtt的消息推送,整理一下代码,代码的原型是网上找的,具体哪个地址已经忘记了. 代码的实现是新建了一个MyMqttService,全部功能都在里面实现,包括连服务器,断线重连,订阅消 ...

随机推荐

  1. 线程入门之start()和run()的区别

    package com.thread; /** * start()和run()的区别 * start():并行执行 * run():方法调用,顺序执行 * @author 95Yang */ publ ...

  2. php工具 phpstorm 的快捷键 的使用(待添加

    参考网址:http://www.cnblogs.com/jikey/p/3491798.html 1. ctrl+tab 键,可以切换各个选项卡 页面 2. shift+enter 键,无论光标在本行 ...

  3. Redis基础知识之—— 缓存应用场景

    转载原文:http://www.cnblogs.com/jinshengzhi/articles/5225718.html 一.MySql+Memcached架构的问题 Memcached采用客户端- ...

  4. Runtime.getRuntime().addShutdownHook

      Runtime.getRuntime().addShutdownHook(shutdownHook); 这个方法的含义说明: 这个方法的意思就是在jvm中增加一个关闭的钩子,当jvm关闭的时候,会 ...

  5. sscanf的用法(转)

    队长做上海邀请赛的I题时遇到一个棘手的问题,字符串的处理很麻烦,按传统的gets全部读入的话还要做N多处理,太浪费时间. 回来之后搜了一下sscanf的用法发现可以很好的解决这一类问题,各种百度,转来 ...

  6. VIM跳到指定行

    ngg nG :n n is the line number

  7. Java 14 类型信息

    14 类型信息 运行是识别对象和类的信息 两种方式RTTI 假定编译时已经知道所有的类型反射 运行时发现和使用类的信息 1 RTTI //多态 创建一个具体的对象(Circle Square Tria ...

  8. Velocity教程 (zhuan)

    http://blog.csdn.net/qq_25237663/article/details/52262532 ****************************************** ...

  9. linux 后台执行命令

    本文主要内容: 1. 设置ctontab文件,并用它来提交作业. 2. 使用at命令来提交作业. 3. 在后台提交作业. 4. 使用nohup命令提交作业. 名词解释: cron 系统调度进程.可以使 ...

  10. JAVA EE 第一阶段考试

    在第一阶段中我们学习了Spring Struts2 Hibernate.虽然在外面的公司中,公司项目的框架中都不在使用Struts2了.他好像出现了不可修复的bug.但是在学校,依然还是要学习这个.在 ...