基于netty实现的长连接,心跳机制及重连机制
概述
详细
详细
本篇demo实现的功能是基于netty的心跳机制和长连接以及重连机制,最关键的就是通过netty中的 IdleStateHandler
的超时机制来实现心跳和重连 ,然后通过org.msgpack
编码器来实现跨平台数据传输,
实现的功能就是通过Scanner来输入消息得到服务端的回应,超过设定的超时时间就触发超时事件来进行心跳传输,如果服务端宕机客户端就会一直发起重连。
一、运行效果
服务端:
客户端:
二、实现过程
在maven pom文件添加依赖:
- <!-- 解码and编码器 -->
- <!-- https://mvnrepository.com/artifact/org.msgpack/msgpack -->
- <dependency>
- <groupId>org.msgpack</groupId>
- <artifactId>msgpack</artifactId>
- <version>0.6.12</version>
- </dependency>
- <!-- netty 核心依赖 -->
- <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
- <dependency>
- <groupId>io.netty</groupId>
- <artifactId>netty-all</artifactId>
- <version>4.1.33.Final</version>
- </dependency>
- <!-- 解码and编码器 -->
- 导入以上依赖
- ↓
- 创建配置模型model(模型类) , TypeData(参数配置类)
- ↓
- 创建解码and编码器MsgPckDecode(解码器) ,MsgPckEncode(编码器)
- ↓
- 创建各自的控制器 AbstractClientChannelInboundHandleAdapter,AbstractServerChannelInboundHandleAdapter
- ↓
- 创建客户端及客户端控制器Client(客户端启动类) , ClientHandler(客户端控制器)
- ↓
- 创建服务端以及控制器Server(客户端启动类) , ServerHandler(客户端控制器)
- ps:本demo使用了msgpack , It’s like JSON. but fast and small.
- 导入以上依赖
- package com.zxh.demo.model;
- import java.io.Serializable;
- import org.msgpack.annotation.Message;
- /**
- * 消息类型分离器
- * @author Administrator
- *
- */
- @Message
- public class Model implements Serializable{
- private static final long serialVersionUID = 1L;
- //类型
- private int type;
- //内容
- private String body;
- public String getBody() {
- return body;
- }
- public void setBody(String body) {
- this.body = body;
- }
- public int getType() {
- return type;
- }
- public void setType(int type) {
- this.type = type;
- }
- @Override
- public String toString() {
- return "Model [type=" + type + ", body=" + body + "]";
- }
- }
- 编写一个配置类接口,用于控制心跳包和应用消息的处理
- package com.zxh.demo.model;
- /**
- * 配置项
- * @author Administrator
- *
- */
- public interface TypeData {
- byte PING = 1;
- byte PONG = 2;
- //顾客
- byte CUSTOMER = 3;
- }
创建MsgPckDecode(解码器)
- package com.zxh.demo.model;
- import java.util.List;
- import org.msgpack.MessagePack;
- import io.netty.buffer.ByteBuf;
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.handler.codec.MessageToMessageDecoder;
- /**
- * 解码器
- * @author Administrator
- *
- */
- public class MsgPckDecode extends MessageToMessageDecoder<ByteBuf>{
- @Override
- protected void decode(ChannelHandlerContext ctx, ByteBuf msg,
- List<Object> out) throws Exception {
- final byte[] array;
- final int length = msg.readableBytes();
- array = new byte[length];
- msg.getBytes(msg.readerIndex(), array, 0, length);
- MessagePack pack = new MessagePack();
- out.add(pack.read(array, Model.class));
- }
- }
- 创建MsgPckEncode(编码器)
- package com.zxh.demo.model;
- import org.msgpack.MessagePack;
- import io.netty.buffer.ByteBuf;
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.handler.codec.MessageToByteEncoder;
- /**
- * 编码器
- * @author Administrator
- *
- */
- public class MsgPckEncode extends MessageToByteEncoder<Object>{
- @Override
- protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf buf)
- throws Exception {
- // TODO Auto-generated method stub
- MessagePack pack = new MessagePack();
- byte[] write = pack.write(msg);
- buf.writeBytes(write);
- }
- }
- 创建client客户端:
- package com.zxh.demo.client;
- import java.util.Scanner;
- import java.util.concurrent.TimeUnit;
- import com.zxh.demo.model.Model;
- import com.zxh.demo.model.MsgPckDecode;
- import com.zxh.demo.model.MsgPckEncode;
- import com.zxh.demo.model.TypeData;
- import io.netty.bootstrap.Bootstrap;
- import io.netty.channel.Channel;
- import io.netty.channel.ChannelFuture;
- import io.netty.channel.ChannelFutureListener;
- import io.netty.channel.ChannelInitializer;
- import io.netty.channel.ChannelOption;
- import io.netty.channel.ChannelPipeline;
- import io.netty.channel.nio.NioEventLoopGroup;
- import io.netty.channel.socket.nio.NioSocketChannel;
- import io.netty.handler.timeout.IdleStateHandler;
- public class Client {
- private NioEventLoopGroup worker = new NioEventLoopGroup();
- private Channel channel;
- private Bootstrap bootstrap;
- public static void main(String[] args) {
- Client client = new Client();
- client.start();
- client.sendData();
- }
- private void start() {
- bootstrap = new Bootstrap();
- bootstrap.group(worker)
- .channel(NioSocketChannel.class)
- .option(ChannelOption.TCP_NODELAY, true)
- .handler(new ChannelInitializer<Channel>() {
- @Override
- protected void initChannel(Channel ch) throws Exception {
- // TODO Auto-generated method stub
- ChannelPipeline pipeline = ch.pipeline();
- pipeline.addLast(new IdleStateHandler(0,0,5));
- pipeline.addLast(new MsgPckDecode());
- pipeline.addLast(new MsgPckEncode());
- pipeline.addLast(new ClientHandler(Client.this));
- }
- });
- doConnect();
- }
- /**
- * 连接服务端 and 重连
- */
- protected void doConnect() {
- if (channel != null && channel.isActive()){
- return;
- }
- ChannelFuture connect = bootstrap.connect("127.0.0.1", 8081);
- //实现监听通道连接的方法
- connect.addListener(new ChannelFutureListener() {
- @Override
- public void operationComplete(ChannelFuture channelFuture) throws Exception {
- if(channelFuture.isSuccess()){
- channel = channelFuture.channel();
- System.out.println("连接服务端成功");
- }else{
- System.out.println("每隔2s重连....");
- channelFuture.channel().eventLoop().schedule(new Runnable() {
- @Override
- public void run() {
- doConnect();
- }
- },2,TimeUnit.SECONDS);
- }
- }
- });
- }
- /**
- * 向服务端发送消息
- */
- private void sendData() {
- Scanner sc= new Scanner(System.in);
- for (int i = 0; i < 1000; i++) {
- if(channel != null && channel.isActive()){
- //获取一个键盘扫描器
- String nextLine = sc.nextLine();
- Model model = new Model();
- model.setType(TypeData.CUSTOMER);
- model.setBody(nextLine);
- channel.writeAndFlush(model);
- }
- }
- }
- }
- 创建Server服务端:
- package com.zxh.demo.server;
- import com.zxh.demo.model.MsgPckDecode;
- import com.zxh.demo.model.MsgPckEncode;
- import io.netty.bootstrap.ServerBootstrap;
- import io.netty.channel.Channel;
- import io.netty.channel.ChannelFuture;
- import io.netty.channel.ChannelInitializer;
- import io.netty.channel.ChannelPipeline;
- import io.netty.channel.EventLoopGroup;
- import io.netty.channel.nio.NioEventLoopGroup;
- import io.netty.channel.socket.nio.NioServerSocketChannel;
- import io.netty.handler.timeout.IdleStateHandler;
- public class Server {
- public static void main(String[] args) {
- EventLoopGroup bossGroup = new NioEventLoopGroup(1);
- EventLoopGroup workerGroup = new NioEventLoopGroup(4);
- try {
- ServerBootstrap serverBootstrap = new ServerBootstrap();
- serverBootstrap.group(bossGroup, workerGroup)
- .channel(NioServerSocketChannel.class)
- .localAddress(8081)
- .childHandler(new ChannelInitializer<Channel>() {
- @Override
- protected void initChannel(Channel ch) throws Exception {
- // TODO Auto-generated method stub
- ChannelPipeline pipeline = ch.pipeline();
- pipeline.addLast(new IdleStateHandler(10,0,0));
- pipeline.addLast(new MsgPckDecode());
- pipeline.addLast(new MsgPckEncode());
- pipeline.addLast(new ServerHandler());
- }
- });
- System.out.println("start server by port 8081 --");
- ChannelFuture sync = serverBootstrap.bind().sync();
- sync.channel().closeFuture().sync();
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }finally{
- //优雅的关闭资源
- bossGroup.shutdownGracefully();
- workerGroup.shutdownGracefully();
- }
- }
- }
- package com.zxh.demo.server;
先运行服务端,然后再启动客户端 会根据设置的端口连接服务端,在客户端输入消息就会得到服务端的回应,如果超过5秒没有进行读写就会触发IdleStateHandler
类超时事件 来进行心跳包的传输 ,服务端未检测到客户端的读写或者心跳就会主动关闭channel通道
三、项目结构图
四、补充
所谓的心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性.因为网络的不可靠性, 有可能在 TCP 保持长连接的过程中, 由于某些突发情况, 例如网线被拔出, 突然掉电等, 会造成服务器和客户端的连接中断. 在这些突发情况下, 如果恰好服务器和客户端之间没有交互的话, 那么它们是不能在短时间内发现对方已经掉线的. 为了解决这个问题, 我们就需要引入 心跳 机制. 心跳机制的工作原理是: 在服务器和客户端之间一定时间内没有数据交互时, 即处于 idle 状态时, 客户端或服务器会发送一个特殊的数据包给对方, 当接收方收到这个数据报文后, 也立即发送一个特殊的数据报文, 回应发送方, 此即一个 PING-PONG 交互. 自然地, 当某一端收到心跳消息后, 就知道了对方仍然在线, 这就确保 TCP 连接的有效性
注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权
基于netty实现的长连接,心跳机制及重连机制的更多相关文章
- 转 互联网推送服务原理:长连接+心跳机制(MQTT协议)
http://blog.csdn.net/zhangzeyuaaa/article/details/39028369 目录(?)[-] 无线移动网络的特点 android系统的推送和IOS的推送有什么 ...
- 互联网推送服务原理:长连接+心跳机制(MQTT协议)
互联网推送消息的方式很常见,特别是移动互联网上,手机每天都能收到好多推送消息,经过研究发现,这些推送服务的原理都是维护一个长连接(要不不可能达到实时效果),但普通的socket连接对服务器的消耗太大了 ...
- 移动互联网消息推送原理:长连接+心跳机制(MQTT协议)
互联网推送消息的方式很常见,特别是移动互联网上,手机每天都能收到好多推送消息,经过研究发现,这些推送服务的原理都是维护一个长连接(要不不可能达到实时效果),但普通的socket连接对服务器的消耗太大了 ...
- [转]Android TCP长连接 心跳机制及实现
背景知识 智能手机上的长连接心跳和在Internet上的长连接心跳有什么不同 Android系统的推送和iOS的推送有什么区别 几种推送的实现方式 协议 1XMPP简介 2 MQTT简介 3移动端消息 ...
- 基于Netty的IdleStateHandler实现Mqtt心跳
基于Netty的IdleStateHandler实现Mqtt心跳 IdleStateHandler解析 最近研究jetlinks编写的基于Netty的mqtt-client(https://githu ...
- 网络编程懒人入门(八):手把手教你写基于TCP的Socket长连接
本文原作者:“水晶虾饺”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.引言 好多小白初次接触即时通讯(比如:IM或者消息推送应用)时,总是不 ...
- 【Netty】利用Netty实现心跳检测和重连机制
一.前言 心跳机制是定时发送一个自定义的结构体(心跳包),让对方知道自己还活着,以确保连接的有效性的机制. 我们用到的很多框架都用到了心跳检测,比如服务注册到 Eureka Server 之后会维 ...
- 网络编程Netty IoT百万长连接优化
目录 IoT推送系统 IoT是什么 IoT推送系统的设计 心跳检测机制 简述心跳检测 心跳检测机制代码示例 百万长连接优化 连接优化代码示例 TCP连接四元组 配置优化 IoT推送系统 IoT是什么 ...
- Netty(一) SpringBoot 整合长连接心跳机制
前言 Netty 是一个高性能的 NIO 网络框架,本文基于 SpringBoot 以常见的心跳机制来认识 Netty. 最终能达到的效果: 客户端每隔 N 秒检测是否需要发送心跳. 服务端也每隔 N ...
随机推荐
- Spring(二)IOC底层实现原理
IOC原理 将对象创建交给Spring去管理. 实现IOC的两种方式 IOC配置文件的方式 IOC注解的方式 IOC底层实现原理 底层实现使用的技术 1.1 xml配置文件 1.2 dom4j解析xm ...
- Linux上iptables防火墙的基本应用教程
iptables是Linux上常用的防火墙软件,下面vps侦探给大家说一下iptables的安装.清除iptables规则.iptables只开放指定端口.iptables屏蔽指定ip.ip段及解封. ...
- window下的计划任务
0x00前言: 这几天看了看信息安全就业的面试题,其中有一条是计划任务如何设置,好几个月前稍微接触了,但是很久没用差不多都忘了>_<,这里就稍微学习下windows的计划任务 写着写着就偏 ...
- P3810 -三维偏序(陌上花开)cdq-分治
P3810 [模板]三维偏序(陌上花开) 思路 :按照 1维排序 二维 分治三维树状数组维护 #include<bits/stdc++.h> using namespace std; #d ...
- CentOS 7 安装MongoDB详细步骤
创建/etc/yum.repos.d/mongodb-org-4.0.repo文件,编辑内容如下: [mongodb-org-4.0] name=MongoDB Repository baseurl= ...
- BZOJ2670 : Almost
求出前缀和$s[]$,那么区间$[l,r]$的几乎平均数$=\frac{s[r]-s[l-1]}{r-l}$. 若只有一个询问,那么可以维护$(i,s[i-1])$的凸壳,在凸壳上二分点$(i,s[i ...
- [P4994]终于结束的起点 (递推)
终于结束的起点 终于写下句点 终于我们告别 终于我们又回到原点 …… 一个个 OIer 的竞赛生涯总是从一场 NOIp 开始,大多也在一场 NOIp 中结束,好似一次次轮回在不断上演. 如果这次 NO ...
- ES6 类
ES6之前没有类的概念,一般采用以下方式来模仿类 基本的类声明语法 私有属性是实例中的属性,不会出现在原型上,且只能在类的构造函数中创建所有私有属性 PersonClass声明实际上创建了一个具有构造 ...
- PAT基础6-3
6-3 简单求和 (10 分) 本题要求实现一个函数,求给定的N个整数的和. 函数接口定义: int Sum ( int List[], int N ); 其中给定整数存放在数组List[]中,正整数 ...
- Maven实战(八)——常用Maven插件介绍(下)
我们都知道Maven本质上是一个插件框架,它的核心并不执行任何具体的构建任务,所有这些任务都交给插件来完成,例如编译源代码是由maven- compiler-plugin完成的.进一步说,每个任务对应 ...