Netty4 Tcp长连接、断开重连、心跳监测、Msgpack编码解码
来源:互联网 发布:沃尔沃销售数据 编辑:程序博客网 时间:2024/06/01 10:49
Netty4 Tcp长连接、断开重连、心跳监测、Msgpack编码解码
结构:
目录:
- 导入Msgpack
- 创建对象,命名DeviceValue
- 创建type标记,命名TypeData
- 创建msgpack编码器
- 创建msgpack解码器
- 创建相同Server端和Client端共有的消息处理类
- 创建Client端消息处理类
- 创建Server端消息处理类
- 创建Client
- 创建Server
- 运行
- 结果
- 导入Msgpack
compile 'org.msgpack:msgpack:0.6.12'
- 创建对象,命名DeviceValue(注意:需要在对象头上注入@Message)
package com.zmm.netty4msgpacktest.domain;import org.msgpack.annotation.Message;@Messagepublic class DeviceValue { private int type; private int seatId; private int speed; private int angle; public int getType() { return type; } public DeviceValue() { } public void setType(int type) { this.type = type; } public int getSeatId() { return seatId; } public void setSeatId(int seatId) { this.seatId = seatId; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } public int getAngle() { return angle; } public void setAngle(int angle) { this.angle = angle; } @Override public String toString() { return "DeviceValue{" + "type=" + type + ", seatId=" + seatId + ", speed=" + speed + ", angle=" + angle + '}'; }}
- 创建type标记,命名TypeData(目的:通过DeviceValue中的不同type来区分数据类型)
package com.zmm.netty4msgpacktest.domain;public interface TypeData { //模式 byte PING = 1; byte PONG = 2; byte CUSTOME = 3; //******************************* byte PING_SEAT = 100; byte PONG_SEAT = 101; byte SERVER_RESPONSE = 102; byte SERVER_RESISTANT = 103;}
- 创建msgpack编码器(Msgpack的具体用法可以去查一下,网上很多,其传输效率真的很高,很适合大量数据传递的情况)
package com.zmm.netty4msgpacktest.code;import org.msgpack.MessagePack;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.MessageToByteEncoder;public class MsgPackEncode extends MessageToByteEncoder<Object> { @Override protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception { MessagePack msgPack = new MessagePack(); byte[] raw = msgPack.write(msg); out.writeBytes(raw); }}
- 创建msgpack解码器
package com.zmm.netty4msgpacktest.code;import com.zmm.netty4msgpacktest.domain.DeviceValue;import org.msgpack.MessagePack;import java.util.List;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.MessageToMessageDecoder;public class MsgPackDecode 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 msgpack=new MessagePack(); out.add(msgpack.read(array, DeviceValue.class)); }}
- 创建相同Server端和Client端共有的消息处理类(通过传递的type,执行不同的逻辑)
package com.zmm.netty4msgpacktest.common;import com.zmm.netty4msgpacktest.domain.DeviceValue;import com.zmm.netty4msgpacktest.domain.TypeData;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import io.netty.handler.timeout.IdleStateEvent;public abstract class CustomHeartbeatHandler extends ChannelInboundHandlerAdapter { protected String name; private int heartbeatCount = 0; public CustomHeartbeatHandler(String name) { this.name = name; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { DeviceValue deviceValue = (DeviceValue) msg; int type = deviceValue.getType(); System.out.println("CustomHeartbeatHandler type="+type); switch (type){ case 1: sendPongMsg(ctx); break; case 2: System.out.println(name + " get pong msg from " + ctx.channel().remoteAddress()); break; case 3: handleData(ctx, msg); break; } } protected void sendPingMsg(ChannelHandlerContext context) { DeviceValue deviceValue = new DeviceValue(); deviceValue.setType(TypeData.PING); deviceValue.setSpeed(0); deviceValue.setAngle(15); deviceValue.setSeatId(TypeData.PING_SEAT); context.channel().writeAndFlush(deviceValue); heartbeatCount++; System.out.println(name + " sent ping msg to " + context.channel().remoteAddress() + ", count: " + heartbeatCount); } private void sendPongMsg(ChannelHandlerContext context) { DeviceValue deviceValue = new DeviceValue(); deviceValue.setType(TypeData.PONG); deviceValue.setSpeed(0); deviceValue.setAngle(15); deviceValue.setSeatId(TypeData.PONG_SEAT); context.channel().writeAndFlush(deviceValue); heartbeatCount++; System.out.println(name + " sent pong msg to " + context.channel().remoteAddress() + ", count: " + heartbeatCount); } protected abstract void handleData(ChannelHandlerContext channelHandlerContext, Object msg); @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { // IdleStateHandler 所产生的 IdleStateEvent 的处理逻辑. if (evt instanceof IdleStateEvent) { IdleStateEvent e = (IdleStateEvent) evt; switch (e.state()) { case READER_IDLE: handleReaderIdle(ctx); break; case WRITER_IDLE: handleWriterIdle(ctx); break; case ALL_IDLE: handleAllIdle(ctx); break; default: break; } } } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.err.println("---" + ctx.channel().remoteAddress() + " is active---"); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.err.println("---" + ctx.channel().remoteAddress() + " is inactive---"); } protected void handleReaderIdle(ChannelHandlerContext ctx) { System.err.println("---READER_IDLE---"); } protected void handleWriterIdle(ChannelHandlerContext ctx) { System.err.println("---WRITER_IDLE---"); } protected void handleAllIdle(ChannelHandlerContext ctx) { System.err.println("---ALL_IDLE---"); }}
- 创建Client端消息处理类(因为在Client里面每隔2s发送一次数据给服务器,这里接收后就不发送了)
package com.zmm.netty4msgpacktest.client;import com.zmm.netty4msgpacktest.common.CustomHeartbeatHandler;import com.zmm.netty4msgpacktest.domain.DeviceValue;import com.zmm.netty4msgpacktest.domain.TypeData;import io.netty.channel.ChannelHandlerContext;public class ClientHandler extends CustomHeartbeatHandler { private Client client; public ClientHandler(Client client) { super("client"); this.client = client; } @Override protected void handleData(ChannelHandlerContext channelHandlerContext, Object msg) { DeviceValue deviceValue = (DeviceValue) msg; System.out.println("client 接收数据:"+deviceValue.toString());// DeviceValue s = new DeviceValue();// s.setType(TypeData.CUSTOME);// s.setSpeed(0);// s.setAngle(15);// s.setSeatId(TypeData.SERVER_RESPONSE);// channelHandlerContext.writeAndFlush(s); } @Override protected void handleAllIdle(ChannelHandlerContext ctx) { super.handleAllIdle(ctx); sendPingMsg(ctx); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { super.channelInactive(ctx); client.doConnect(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println(name + " exception"+cause.toString()); }}
- 创建Server端消息处理类(服务端收到数据后,返回固定数据给客户端)
package com.zmm.netty4msgpacktest.server;import com.zmm.netty4msgpacktest.common.CustomHeartbeatHandler;import com.zmm.netty4msgpacktest.domain.DeviceValue;import com.zmm.netty4msgpacktest.domain.TypeData;import io.netty.channel.ChannelHandlerContext;public class ServerHandler extends CustomHeartbeatHandler { public ServerHandler() { super("server"); } @Override protected void handleData(ChannelHandlerContext channelHandlerContext, Object msg) { DeviceValue deviceValue = (DeviceValue) msg; System.out.println("server 接收数据:"+deviceValue.toString()); DeviceValue s = new DeviceValue(); s.setType(TypeData.CUSTOME); s.setSpeed(0); s.setAngle(15); s.setSeatId(TypeData.SERVER_RESPONSE); channelHandlerContext.writeAndFlush(s); System.out.println("server 发送数据:"+s.toString()); } @Override protected void handleReaderIdle(ChannelHandlerContext ctx) { super.handleReaderIdle(ctx); System.err.println("---client " + ctx.channel().remoteAddress().toString() + " reader timeout, close it---"); ctx.close(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println(name+" exception"+cause.toString()); }}
- 创建Client(这里执行了多个程序,包括连接服务器、重连、定时发送等)
package com.zmm.netty4msgpacktest.client;import com.zmm.netty4msgpacktest.code.MsgPackDecode;import com.zmm.netty4msgpacktest.code.MsgPackEncode;import com.zmm.netty4msgpacktest.domain.DeviceValue;import com.zmm.netty4msgpacktest.domain.TypeData;import java.util.Random;import java.util.concurrent.TimeUnit;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.ChannelPipeline;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioSocketChannel;import io.netty.handler.codec.LengthFieldBasedFrameDecoder;import io.netty.handler.timeout.IdleStateHandler;public class Client { private NioEventLoopGroup workGroup = new NioEventLoopGroup(4); private Channel channel; private Bootstrap bootstrap; public static void main(String[] args) throws Exception { Client client = new Client(); client.start(); client.sendData(); } public void start() { try { bootstrap = new Bootstrap(); bootstrap .group(workGroup) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline p = socketChannel.pipeline(); p.addLast(new IdleStateHandler(0, 0, 5));// p.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 4, -4, 0)); p.addLast(new MsgPackDecode()); p.addLast(new MsgPackEncode()); p.addLast(new ClientHandler(Client.this)); } }); doConnect(); } catch (Exception e) { throw new RuntimeException(e); } } /** * 重连机制,每隔2s重新连接一次服务器 */ protected void doConnect() { if (channel != null && channel.isActive()) { return; } ChannelFuture future = bootstrap.connect("127.0.0.1", 12345); future.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture futureListener) throws Exception { if (futureListener.isSuccess()) { channel = futureListener.channel(); System.out.println("Connect to server successfully!"); } else { System.out.println("Failed to connect to server, try connect after 2s"); futureListener.channel().eventLoop().schedule(new Runnable() { @Override public void run() { doConnect(); } }, 2, TimeUnit.SECONDS); } } }); } /** * 发送数据 每隔2秒发送一次 * @throws Exception */ public void sendData() throws Exception { Random random = new Random(System.currentTimeMillis()); for (int i = 0; i < 10000; i++) { if (channel != null && channel.isActive()) { DeviceValue deviceValue = new DeviceValue(); deviceValue.setType(TypeData.CUSTOME); deviceValue.setAngle(i%15); deviceValue.setSeatId(i%30); deviceValue.setSpeed(i%120); System.out.println("client 发送数据:"+deviceValue.toString()); channel.writeAndFlush(deviceValue); } Thread.sleep(random.nextInt(20000)); } }}
- 创建Server(Server开启一次就够了,不需要重复开启,若是服务器断开,Server和Client不会报异常,Client会继续不断的搜索服务器,直到搜索到为止)
package com.zmm.netty4msgpacktest.server;import com.zmm.netty4msgpacktest.code.MsgPackDecode;import com.zmm.netty4msgpacktest.code.MsgPackEncode;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.Channel;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelPipeline;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.codec.LengthFieldBasedFrameDecoder;import io.netty.handler.timeout.IdleStateHandler;public class Server { public static void main(String[] args) { NioEventLoopGroup bossGroup = new NioEventLoopGroup(1); NioEventLoopGroup workGroup = new NioEventLoopGroup(4); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap .group(bossGroup, workGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline p = socketChannel.pipeline(); p.addLast(new IdleStateHandler(10, 0, 0));// p.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 4, -4, 0)); p.addLast(new MsgPackDecode()); p.addLast(new MsgPackEncode()); p.addLast(new ServerHandler()); } }); Channel ch = bootstrap.bind(12345).sync().channel(); System.out.println("------Server Start------"); ch.closeFuture().sync(); } catch (Exception e) { throw new RuntimeException(e); } finally { bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } }}
- 运行
- 结果
Server端日志:
Client端日志:
Github源码:Netty4MsgPackTest
阅读全文
0 0
- Netty4 Tcp长连接、断开重连、心跳监测、Msgpack编码解码
- TCP/IP 长连接 心跳 重连 重发 线程
- 长连接、心跳和断线重连
- 一秒学会安卓tcp基于netty4.x心跳,断线重连,状态监听
- Android IM(五)连接断开重连
- 互联网的长在线、心跳和断线重连
- [转]互联网的长在线、心跳和断线重连
- 互联网的长在线、心跳和断线重连
- 互联网的长在线、心跳和断线重连
- Iptables模块reload问题(解决iptables服务重启导致TCP长连接断开)
- 浅谈TCP/IP 三次握手建立连接,四次握手断开连接,以及关于非阻塞socket重连问题
- android客户端TCP传输数据、重连、心跳检测
- socket长连接,断线重连案例
- websocke心跳重连
- tcp长连接中客户端重连
- TCP的长连接、短连接、keepalive及心跳
- TCP长连接与短连接、心跳机制
- openfire Android学习(五)------连接断开重连
- 阿里顺丰互相拉黑,刘强东却为何要力挺顺丰?
- Android Studio 生成aar包和jar包
- 冒泡排序详解
- 便条-逐行读取文件并对其更改
- 电商底层接口(系统接口或SOA接口)高并发解决方案:dubbo + zookeeper
- Netty4 Tcp长连接、断开重连、心跳监测、Msgpack编码解码
- 通过crond定时任务自动更新动态IP到PubYun(3322)实现远程访问
- iOS多模式&富交互视频播放器TTAVPlayer(附源码)
- servlet上传文件
- 又是一年高考季,看看品牌商如何玩转借势营销?
- 【mysql 密码重置开启远程连接终极版】mysql 安装密码设置开启远程连接
- 在NSObject类中,和继承它的类中,弹出UIAlertcontroller和push、present到其它界面
- CLSIDFromProgID
- MySQL表名不区分大小写的设置方法