技术:maven3.0.5 + netty4.1.33 + jdk1.8
 

概述

Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。 也就是说,Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。 “快速”和“简单”并不用产生维护性或性能上的问题。Netty 是一个吸收了多种协议(包括FTP、SMTP、HTTP等各种二进制文本协议)的实现经验,并经过相当精心设计的项目。最终,Netty 成功的找到了一种方式,在保证易于开发的同时还保证了其应用的性能,稳定性和伸缩性。

详细

详细

本篇demo实现的功能是基于netty的心跳机制和长连接以及重连机制,最关键的就是通过netty中的 IdleStateHandler 的超时机制来实现心跳和重连 ,然后通过org.msgpack编码器来实现跨平台数据传输,

实现的功能就是通过Scanner来输入消息得到服务端的回应,超过设定的超时时间就触发超时事件来进行心跳传输,如果服务端宕机客户端就会一直发起重连。

一、运行效果

服务端:

客户端:

二、实现过程

  1. 在maven pom文件添加依赖:

  2.        <!-- 解码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>
  3. 导入以上依赖

    创建配置模型model(模型类) , TypeData(参数配置类)

    创建解码and编码器MsgPckDecode(解码器) ,MsgPckEncode(编码器)

    创建各自的控制器 AbstractClientChannelInboundHandleAdapter,AbstractServerChannelInboundHandleAdapter

    创建客户端及客户端控制器Client(客户端启动类) , ClientHandler(客户端控制器)

    创建服务端以及控制器Server(客户端启动类) , ServerHandler(客户端控制器) ps:本demo使用了msgpack , It’s like JSON. but fast and small.
  4. 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 + "]";
    }
    }
  5. 编写一个配置类接口,用于控制心跳包和应用消息的处理
  6. package com.zxh.demo.model;
    
    /**
    * 配置项
    * @author Administrator
    *
    */
    public interface TypeData { byte PING = 1; byte PONG = 2;
    //顾客
    byte CUSTOMER = 3;
    }

    创建MsgPckDecode(解码器)

  7. 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)); }
    }
  8. 创建MsgPckEncode(编码器)
  9. 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); }
    }
  10. 创建client客户端:
  11. 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);
    }
    }
    }
    }
  12. 创建Server服务端:
  13. 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();
    }
    }
    }

先运行服务端,然后再启动客户端 会根据设置的端口连接服务端,在客户端输入消息就会得到服务端的回应,如果超过5秒没有进行读写就会触发IdleStateHandler类超时事件 来进行心跳包的传输 ,服务端未检测到客户端的读写或者心跳就会主动关闭channel通道

三、项目结构图

四、补充

所谓的心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性.因为网络的不可靠性, 有可能在 TCP 保持长连接的过程中, 由于某些突发情况, 例如网线被拔出, 突然掉电等, 会造成服务器和客户端的连接中断. 在这些突发情况下, 如果恰好服务器和客户端之间没有交互的话, 那么它们是不能在短时间内发现对方已经掉线的. 为了解决这个问题, 我们就需要引入 心跳 机制. 心跳机制的工作原理是: 在服务器和客户端之间一定时间内没有数据交互时, 即处于 idle 状态时, 客户端或服务器会发送一个特殊的数据包给对方, 当接收方收到这个数据报文后, 也立即发送一个特殊的数据报文, 回应发送方, 此即一个 PING-PONG 交互. 自然地, 当某一端收到心跳消息后, 就知道了对方仍然在线, 这就确保 TCP 连接的有效性

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

基于netty实现的长连接,心跳机制及重连机制的更多相关文章

  1. 转 互联网推送服务原理:长连接+心跳机制(MQTT协议)

    http://blog.csdn.net/zhangzeyuaaa/article/details/39028369 目录(?)[-] 无线移动网络的特点 android系统的推送和IOS的推送有什么 ...

  2. 互联网推送服务原理:长连接+心跳机制(MQTT协议)

    互联网推送消息的方式很常见,特别是移动互联网上,手机每天都能收到好多推送消息,经过研究发现,这些推送服务的原理都是维护一个长连接(要不不可能达到实时效果),但普通的socket连接对服务器的消耗太大了 ...

  3. 移动互联网消息推送原理:长连接+心跳机制(MQTT协议)

    互联网推送消息的方式很常见,特别是移动互联网上,手机每天都能收到好多推送消息,经过研究发现,这些推送服务的原理都是维护一个长连接(要不不可能达到实时效果),但普通的socket连接对服务器的消耗太大了 ...

  4. [转]Android TCP长连接 心跳机制及实现

    背景知识 智能手机上的长连接心跳和在Internet上的长连接心跳有什么不同 Android系统的推送和iOS的推送有什么区别 几种推送的实现方式 协议 1XMPP简介 2 MQTT简介 3移动端消息 ...

  5. 基于Netty的IdleStateHandler实现Mqtt心跳

    基于Netty的IdleStateHandler实现Mqtt心跳 IdleStateHandler解析 最近研究jetlinks编写的基于Netty的mqtt-client(https://githu ...

  6. 网络编程懒人入门(八):手把手教你写基于TCP的Socket长连接

    本文原作者:“水晶虾饺”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.引言 好多小白初次接触即时通讯(比如:IM或者消息推送应用)时,总是不 ...

  7. 【Netty】利用Netty实现心跳检测和重连机制

    一.前言 心跳机制是定时发送一个自定义的结构体(心跳包),让对方知道自己还活着,以确保连接的有效性的机制.   我们用到的很多框架都用到了心跳检测,比如服务注册到 Eureka Server 之后会维 ...

  8. 网络编程Netty IoT百万长连接优化

    目录 IoT推送系统 IoT是什么 IoT推送系统的设计 心跳检测机制 简述心跳检测 心跳检测机制代码示例 百万长连接优化 连接优化代码示例 TCP连接四元组 配置优化 IoT推送系统 IoT是什么 ...

  9. Netty(一) SpringBoot 整合长连接心跳机制

    前言 Netty 是一个高性能的 NIO 网络框架,本文基于 SpringBoot 以常见的心跳机制来认识 Netty. 最终能达到的效果: 客户端每隔 N 秒检测是否需要发送心跳. 服务端也每隔 N ...

随机推荐

  1. 流网络分析系统-SNAS

    流网络分析系统-SNAS SNAS,Streaming Network Analytics System (project SNAS) ,是一个收集.跟踪.存取 千万条实时路由对象的系统. 官网:ht ...

  2. HTML文本结构及常用标签

    一.什么是HTML? HTML:超文本标签语言 (Hyper Text Markup Language) www万维网的描述性语言. XHTML指可扩展超文本标记语言(标识语言)(EXtensible ...

  3. 对于长沙互联网发展,一个外来两年Java程序员的所见所感所愿

    惟楚有材,于斯为盛 本文有感于2019长沙互联网求职招聘大会,内容比较多,但都是我自己的一些所见.所感和所愿. 2019年3月的最后一天,参加2019长沙互联网求职招聘大会,看到了很多的招聘企业,也看 ...

  4. PHP反序列化与Session

    0x00前言: php存储session有三种模式,php_serialize, php, binary 这里着重讨论php_serialize和php的不合理使用导致的安全问题 关于session的 ...

  5. gdb windbg and od use

    gdb aslr -- 显示/设置 gdb 的 ASLR asmsearch -- Search for ASM instructions in memory asmsearch "int ...

  6. 基于Ardalis.GuardClauses守卫组件的拓展

    在我们写程序的时候,经常会需要判断数据的是空值还是null值,基本上十个方法函数,八个要做这样的判断,因此我们很有必要拓展出来一个类来做监控,在这里我们使用一个简单地,可拓展的第三方组件:Ardali ...

  7. 服务端spark gbdt模型计算性能优化

    服务端使用训练出来的模型,spark模型计算第一步是实现spark模型加载. 线上服务对用户体验影响极大,故需要对模型使用进行优化. 1.多线程并发进行计算,线上两个服务.优化cpu 2.在扩召回集, ...

  8. 多AG自动生成apk说明

    -----------自动生成apk说明.txt 1017202130405658626378 自动实现思路几个变化的地方 就是 第一次放入文件10, 修改apktype 100130 10生成apk ...

  9. Centos中安装perl

    1.安装gcc,在虚拟机命令窗口中输入:yum install -y gcc 2.下载perl安装包输入命令:wget http://www.cpan.org/src/5.0/perl-5.16.1. ...

  10. Java基础(十三) 文件高级技术

    文件高级技术 一.常见文件类型处理 一)属性文件 属性文件很简单,一行表示一个属性,属性就是键值对,键和值用(=)或者(:)分隔. #ready to work name = tang age = p ...