心跳是为了保证客户端和服务端的通信可用。因为各种原因客户端和服务端不能及时响应和接收信息。比如网络断开,停电 或者是客户端/服务端 高负载。

所以每隔一段时间 客户端发送心跳包到客户端  服务端做出心跳的响应;

1.如果客户端在指定时间没有向服务端发送心跳包。则表示客户端的通信出现了问题。

2.如果客户端发送心跳包到服务端没有收到响应 则表示服务端的通信出现了问题。

netty提供IdleStateHandle 在监听距离上一次写的时间和距离上一次读的时间 如果超时则调用

源码:

  1. public class IdleStateHandler extends ChannelDuplexHandler
  2. @Override
  3. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  4. // This method will be invoked only if this handler was added
  5. // before channelActive() event is fired. If a user adds this handler
  6. // after the channelActive() event, initialize() will be called by beforeAdd().
  7. initialize(ctx);
  8. super.channelActive(ctx);
  9. }
  10. }
  1. private void initialize(ChannelHandlerContext ctx) {
  2. // Avoid the case where destroy() is called before scheduling timeouts.
  3. // See: https://github.com/netty/netty/issues/143
  4. switch (state) {
  5. case 1:
  6. case 2:
  7. return;
  8. }
  9.  
  10. state = 1;
  11. initOutputChanged(ctx);
  12.  
  13. lastReadTime = lastWriteTime = ticksInNanos();
  14. if (readerIdleTimeNanos > 0) {
  15. readerIdleTimeout = schedule(ctx, new ReaderIdleTimeoutTask(ctx),//监听read的task
  16. readerIdleTimeNanos, TimeUnit.NANOSECONDS);
  17. }
  18. if (writerIdleTimeNanos > 0) {
  19. writerIdleTimeout = schedule(ctx, new WriterIdleTimeoutTask(ctx),//监听写的task
  20. writerIdleTimeNanos, TimeUnit.NANOSECONDS);
  21. }
  22. if (allIdleTimeNanos > 0) {
  23. allIdleTimeout = schedule(ctx, new AllIdleTimeoutTask(ctx),//监听读写的task
  24. allIdleTimeNanos, TimeUnit.NANOSECONDS);
  25. }
  26. }
  1. private final class ReaderIdleTimeoutTask extends AbstractIdleTask {
  2.  
  3. ReaderIdleTimeoutTask(ChannelHandlerContext ctx) {
  4. super(ctx);
  5. }
  6.  
  7. @Override
  8. protected void run(ChannelHandlerContext ctx) {
  9. long nextDelay = readerIdleTimeNanos;
  10. if (!reading) {
  11. nextDelay -= ticksInNanos() - lastReadTime;
  12. }
  13.  
  14. if (nextDelay <= 0) {
  15. // Reader is idle - set a new timeout and notify the callback.
  16. readerIdleTimeout = schedule(ctx, this, readerIdleTimeNanos, TimeUnit.NANOSECONDS);
  17.  
  18. boolean first = firstReaderIdleEvent;
  19. firstReaderIdleEvent = false;
  20.  
  21. try {
  22. IdleStateEvent event = newIdleStateEvent(IdleState.READER_IDLE, first);
  23. channelIdle(ctx, event);
  24. } catch (Throwable t) {
  25. ctx.fireExceptionCaught(t);
  26. }
  27. } else {
  28. // Read occurred before the timeout - set a new timeout with shorter delay.
  29. readerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);
  30. }
  31. }
  32. }
  33.  
  34. private final class WriterIdleTimeoutTask extends AbstractIdleTask {
  35.  
  36. WriterIdleTimeoutTask(ChannelHandlerContext ctx) {
  37. super(ctx);
  38. }
  39.  
  40. @Override
  41. protected void run(ChannelHandlerContext ctx) {
  42.  
  43. long lastWriteTime = IdleStateHandler.this.lastWriteTime;
  44. long nextDelay = writerIdleTimeNanos - (ticksInNanos() - lastWriteTime);
  45. if (nextDelay <= 0) {
  46. // Writer is idle - set a new timeout and notify the callback.
  47. writerIdleTimeout = schedule(ctx, this, writerIdleTimeNanos, TimeUnit.NANOSECONDS);
  48.  
  49. boolean first = firstWriterIdleEvent;
  50. firstWriterIdleEvent = false;
  51.  
  52. try {
  53. if (hasOutputChanged(ctx, first)) {
  54. return;
  55. }
  56.  
  57. IdleStateEvent event = newIdleStateEvent(IdleState.WRITER_IDLE, first);
  58. channelIdle(ctx, event);
  59. } catch (Throwable t) {
  60. ctx.fireExceptionCaught(t);
  61. }
  62. } else {
  63. // Write occurred before the timeout - set a new timeout with shorter delay.
  64. writerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);
  65. }
  66. }
  67. }
  68.  
  69. private final class AllIdleTimeoutTask extends AbstractIdleTask {
  70.  
  71. AllIdleTimeoutTask(ChannelHandlerContext ctx) {
  72. super(ctx);
  73. }
  74.  
  75. @Override
  76. protected void run(ChannelHandlerContext ctx) {
  77.  
  78. long nextDelay = allIdleTimeNanos;
  79. if (!reading) {
  80. nextDelay -= ticksInNanos() - Math.max(lastReadTime, lastWriteTime);
  81. }
  82. if (nextDelay <= 0) {
  83. // Both reader and writer are idle - set a new timeout and
  84. // notify the callback.
  85. allIdleTimeout = schedule(ctx, this, allIdleTimeNanos, TimeUnit.NANOSECONDS);
  86.  
  87. boolean first = firstAllIdleEvent;
  88. firstAllIdleEvent = false;
  89.  
  90. try {
  91. if (hasOutputChanged(ctx, first)) {
  92. return;
  93. }
  94.  
  95. IdleStateEvent event = newIdleStateEvent(IdleState.ALL_IDLE, first);
  96. channelIdle(ctx, event);
  97. } catch (Throwable t) {
  98. ctx.fireExceptionCaught(t);
  99. }
  100. } else {
  101. // Either read or write occurred before the timeout - set a new
  102. // timeout with shorter delay.
  103. allIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);
  104. }
  105. }
  106. }

三个内部类是IdleSateHandle的内部类 可以看到内部是通过另起一个线程进行监听上一次对应事件的触发 如果超时则调用对应的事件

基于三的代码进行修改

首先是MessageHead消息头增加消息类型

  1. public class MessageHead {
  2. private int headData=0X76;//协议开始标志
  3. private int length;//包的长度
  4. private String token;
  5. private Date createDate;
  6. private String type;//消息类型 ping表示心跳包
  7. public int getHeadData() {
  8. return headData;
  9. }
  10. public void setHeadData(int headData) {
  11. this.headData = headData;
  12. }
  13. public int getLength() {
  14. return length;
  15. }
  16. public void setLength(int length) {
  17. this.length = length;
  18. }
  19.  
  20. public String getToken() {
  21. return token;
  22. }
  23. public void setToken(String token) {
  24. this.token = token;
  25. }
  26. public Date getCreateDate() {
  27. return createDate;
  28. }
  29. public void setCreateDate(Date createDate) {
  30. this.createDate = createDate;
  31. }
  32.  
  33. public String getType() {
  34. return type;
  35. }
  36. public void setType(String type) {
  37. this.type = type;
  38. }
  39. @Override
  40. public String toString() {
  41. SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  42. // TODO Auto-generated method stub
  43. return "headData:"+headData+",length:"+length+",token:"+token+",createDate:"+ simpleDateFormat.format(createDate);
  44. }
  45. }

MessageDecode

  1. package com.liqiang.SimpeEcode;
  2.  
  3. import java.text.SimpleDateFormat;
  4. import java.util.List;
  5. import com.liqiang.nettyTest2.nettyMain;
  6.  
  7. import io.netty.buffer.ByteBuf;
  8. import io.netty.channel.ChannelHandlerContext;
  9. import io.netty.handler.codec.ByteToMessageDecoder;
  10. import io.netty.handler.codec.MessageToByteEncoder;
  11. import io.netty.handler.codec.MessageToMessageDecoder;
  12.  
  13. public class MessageDecode extends ByteToMessageDecoder{
  14.  
  15. private final int BASE_LENGTH=4+4+50+50+50;//协议头 类型 int+length 4个字节+消息类型加令牌和 令牌生成时间50个字节
  16. private int headData=0X76;//协议开始标志
  17.  
  18. @Override
  19. protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
  20. // 刻度长度必须大于基本长度
  21. if(buffer.readableBytes()>=BASE_LENGTH) {
  22. /**
  23. * 粘包 发送频繁 可能多次发送黏在一起 需要考虑 不过一个客户端发送太频繁也可以推断是否是攻击
  24. */
  25. //防止soket流攻击。客户端传过来的数据太大不合理
  26. if(buffer.readableBytes()>2048) {
  27. //buffer.skipBytes(buffer.readableBytes());
  28.  
  29. }
  30. }
  31. int beginIndex;//记录包开始位置
  32. while(true) {
  33. // 获取包头开始的index
  34. beginIndex = buffer.readerIndex();
  35. //如果读到开始标记位置 结束读取避免拆包和粘包
  36. if(buffer.readInt()==headData) {
  37. break;
  38. }
  39.  
  40. //初始化读的index为0
  41. buffer.resetReaderIndex();
  42. // 当略过,一个字节之后,
  43. //如果当前buffer数据小于基础数据 返回等待下一次读取
  44. if (buffer.readableBytes() < BASE_LENGTH) {
  45. return;
  46. }
  47. }
  48. // 消息的长度
  49. int length = buffer.readInt();
  50. // 判断请求数据包数据是否到齐 -150是消息头的长度。
  51. if ((buffer.readableBytes()-) < length) {
  52. //没有到齐 返回读的指针 等待下一次数据到期再读
  53. buffer.readerIndex(beginIndex);
  54. return;
  55. }
  56. //读取消息类型
  57. byte[] typeByte=new byte[50];
  58. buffer.readBytes(typeByte);
  59. //读取令牌
  60. byte[] tokenByte=new byte[50];
  61. buffer.readBytes(tokenByte);
  62.  
  63. //读取令牌生成时间
  64. byte[]createDateByte=new byte[50];
  65. buffer.readBytes(createDateByte);
  66. //读取content
  67. byte[] data = new byte[length];
  68. buffer.readBytes(data);
  69. MessageHead head=new MessageHead();
  70. head.setHeadData(headData);
  71. head.setToken(new String(tokenByte).trim());
  72. SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  73. head.setCreateDate( simpleDateFormat.parse(new String(createDateByte).trim()));
  74. head.setLength(length);
  75. head.setType(new String(typeByte).trim());
  76. Message message=new Message(head, data);
  77. //认证不通过
  78. if(!message.authorization(message.buidToken())) {
  79. ctx.close();
  80.  
  81. return;
  82. }
  83. out.add(message);
  84. buffer.discardReadBytes();//回收已读字节
  85. }
  86.  
  87. }

MessageEncoder

  1. package com.liqiang.SimpeEcode;
  2.  
  3. import java.text.SimpleDateFormat;
  4. import java.util.Arrays;
  5. import java.util.Calendar;
  6.  
  7. import io.netty.buffer.ByteBuf;
  8. import io.netty.channel.ChannelHandlerContext;
  9. import io.netty.handler.codec.MessageToByteEncoder;
  10.  
  11. public class MessageEncoder extends MessageToByteEncoder<Message> {
  12.  
  13. @Override
  14. protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception {
  15. // TODO Auto-generated method stub
  16. // 写入开头的标志
  17. out.writeInt(msg.getHead().getHeadData());
  18. // 写入包的的长度
  19. out.writeInt(msg.getContent().length);
  20. byte[] typeByte = new byte[50];
  21. /**
  22. * type定长50个字节
  23. * 第一个参数 原数组
  24. * 第二个参数 原数组位置
  25. * 第三个参数 目标数组
  26. * 第四个参数 目标数组位置
  27. * 第五个参数 copy多少个长度
  28. */
  29. byte[] indexByte=msg.getHead().getType().getBytes();
  30. try {
  31. System.arraycopy(indexByte, 0, typeByte, 0,indexByte.length>typeByte.length?typeByte.length:indexByte.length);
  32. } catch (Exception e) {
  33. // TODO: handle exception
  34. e.printStackTrace();
  35. }
  36. //写入消息类型
  37. out.writeBytes(typeByte);
  38. byte[] tokenByte = new byte[50];
  39. /**
  40. * token定长50个字节
  41. * 第一个参数 原数组
  42. * 第二个参数 原数组位置
  43. * 第三个参数 目标数组
  44. * 第四个参数 目标数组位置
  45. * 第五个参数 copy多少个长度
  46. */
  47. indexByte=msg.getHead().getToken().getBytes();
  48. try {
  49. System.arraycopy(indexByte, 0, tokenByte, 0,indexByte.length>tokenByte.length?tokenByte.length:indexByte.length);
  50. } catch (Exception e) {
  51. // TODO: handle exception
  52. e.printStackTrace();
  53. }
  54.  
  55. //写入令牌
  56. out.writeBytes(tokenByte);
  57. byte[] createTimeByte = new byte[50];
  58. SimpleDateFormat format0 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  59. String time = format0.format(msg.getHead().getCreateDate());
  60. indexByte=time.getBytes();
  61. System.arraycopy(indexByte, 0, createTimeByte, 0,indexByte.length>createTimeByte.length?createTimeByte.length:indexByte.length);
  62. //写入令牌生成时间
  63. out.writeBytes(createTimeByte);
  64.  
  65. // 写入消息主体
  66. out.writeBytes(msg.getContent());
  67.  
  68. }
  69.  
  70. }

红色部分为改动部分

ClientChannelInitializer

  1. package com.liqiang.nettyTest2;
  2.  
  3. import java.util.concurrent.TimeUnit;
  4.  
  5. import com.liqiang.SimpeEcode.MessageDecode;
  6. import com.liqiang.SimpeEcode.MessageEncoder;
  7.  
  8. import io.netty.channel.ChannelInitializer;
  9. import io.netty.channel.socket.SocketChannel;
  10. import io.netty.handler.codec.string.StringDecoder;
  11. import io.netty.handler.codec.string.StringEncoder;
  12. import io.netty.handler.timeout.IdleStateHandler;
  13.  
  14. public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {
  15.  
  16. private Client client;
  17. public ClientChannelInitializer(Client client) {
  18. // TODO Auto-generated constructor stub
  19. this.client=client;
  20. }
  21. @Override
  22. protected void initChannel(SocketChannel socketChannel) throws Exception {
  23. // TODO Auto-generated method stub
  24. socketChannel.pipeline()
  25. //表示5秒向服务器发送一次心跳包 10秒没接收到服务器端信息表示服务器端通信异常 则会触发clientHandle userEventTriggered事件
  26. .addLast("ping",new IdleStateHandler(10, 5, 0, TimeUnit.SECONDS))
  27. .addLast("decoder",new MessageEncoder())
  28. .addLast("encoder",new MessageDecode())
  29. .addLast(new ClientHandle(client));//注册处理器
  30.  
  31. }
  32. }

ClientHandle修改

  1. package com.liqiang.nettyTest2;
  2.  
  3. import java.util.Date;
  4.  
  5. import com.liqiang.SimpeEcode.Message;
  6. import com.liqiang.SimpeEcode.MessageHead;
  7.  
  8. import io.netty.channel.ChannelHandlerAdapter;
  9. import io.netty.channel.ChannelHandlerContext;
  10. import io.netty.channel.ChannelInboundHandlerAdapter;
  11. import io.netty.handler.codec.http.cors.CorsHandler;
  12. import io.netty.handler.timeout.IdleState;
  13. import io.netty.handler.timeout.IdleStateEvent;
  14.  
  15. public class ClientHandle extends ChannelInboundHandlerAdapter {
  16.  
  17. Client client;
  18. public ClientHandle(Client client) {
  19. // TODO Auto-generated constructor stub
  20. this.client=client;
  21. }
  22. /**
  23. * 读写超时事事件
    * IdleStateHandle配置的 如果5秒没有触发writer事件 则会触发 userEventTrigerd方法 我们则写一次心跳
    * 如果10秒没有触发read事件则表示服务器通信异常 因为我们每次发送一次心跳包 服务器都会做出对应的心跳反应
  24. * @throws Exception
  25. */
  26. @Override
  27. public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
  28. if(evt instanceof IdleStateEvent) {
  29. IdleStateEvent idleStateEvent=((IdleStateEvent) evt);
  30. /**
  31. * 如果没有收到服务端的写 则表示服务器超时 判断是否断开连接
  32. */
  33. if(idleStateEvent.state()==IdleState.READER_IDLE) {
  34. System.out.println("服务器无响应");
  35. if(!ctx.channel().isOpen()) {
  36. System.out.println("正在重连");
  37. client.connection();
  38. System.out.println("重连成功");
  39. }
  40. }else if(idleStateEvent.state()==IdleState.WRITER_IDLE) {
  41. //如果没有触发写事件则向服务器发送一次心跳包
  42. System.out.println("正在向服务端发送心跳包");
  43. MessageHead head=new MessageHead();
  44. byte[]content="".getBytes();
  45. head.setCreateDate(new Date());
  46. head.setType("ping");
  47. head.setLength(content.length);
  48. Message pingMessage=new Message(head,content);
  49. head.setToken(pingMessage.buidToken());
  50. ctx.writeAndFlush(pingMessage);
  51. }
  52. }else {
  53. super.userEventTriggered(ctx, evt);
  54. }
  55. }
  56. //建立连接时回调
  57. @Override
  58. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  59. // TODO Auto-generated method stub
  60. //System.out.println("与服务器建立连接成功");
  61. client.setServerChannel(ctx);
  62. client.setConnection(true);
  63. //ctx.fireChannelActive();//如果注册多个handle 下一个handel的事件需要触发需要调用这个方法
  64.  
  65. }
  66. //读取服务器发送信息时回调
  67. @Override
  68. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  69. Message message=(Message) msg;
  70. if(message.getHead().getType().equals("ping")) {
  71. //表示是心跳包 不做任何业务处理
  72. }else {
  73. // TODO Auto-generated method stub
  74. System.out.println(msg.toString());
  75. }
  76.  
  77. }
  78.  
  79. //发生异常时回调
  80. @Override
  81. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  82. // TODO Auto-generated method stub
  83. System.out.println("发生异常 与服务器断开连接");
  84. ctx.close();//关闭连接
  85. }
  86. }

ServerChannelInitializer

  1. package com.liqiang.nettyTest2;
  2.  
  3. import java.util.concurrent.TimeUnit;
  4.  
  5. import com.liqiang.SimpeEcode.MessageDecode;
  6. import com.liqiang.SimpeEcode.MessageEncoder;
  7.  
  8. import io.netty.buffer.Unpooled;
  9. import io.netty.channel.ChannelInitializer;
  10. import io.netty.channel.socket.SocketChannel;
  11. import io.netty.handler.codec.LineBasedFrameDecoder;
  12. import io.netty.handler.codec.AsciiHeadersEncoder.NewlineType;
  13. import io.netty.handler.codec.DelimiterBasedFrameDecoder;
  14. import io.netty.handler.codec.string.StringDecoder;
  15. import io.netty.handler.codec.string.StringEncoder;
  16. import io.netty.handler.timeout.IdleStateHandler;
  17.  
  18. public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
  19.  
  20. private Server server;
  21. public ServerChannelInitializer(Server server) {
  22. this.server=server;
  23. }
  24. @Override
  25. protected void initChannel(SocketChannel channel) throws Exception {
  26. // TODO Auto-generated method stub
  27. channel.pipeline()
  28. //7秒没收到客户端信息 则表示客户端因为网络等原因异常关闭
  29. .addLast("ping",new IdleStateHandler(7, 0, 0,TimeUnit.SECONDS))
  30. .addLast("decoder",new MessageDecode())
  31. .addLast("encoder",new MessageEncoder())
  32. .addLast(new ServerHandle(server));
  33. }
  34.  
  35. }

ServerHandle

  1. package com.liqiang.nettyTest2;
  2.  
  3. import java.util.Date;
  4.  
  5. import com.liqiang.SimpeEcode.Message;
  6. import com.liqiang.SimpeEcode.MessageHead;
  7.  
  8. import io.netty.channel.ChannelHandlerContext;
  9. import io.netty.channel.ChannelInboundHandlerAdapter;
  10. import io.netty.handler.timeout.IdleState;
  11. import io.netty.handler.timeout.IdleStateEvent;
  12. import io.netty.handler.timeout.IdleStateHandler;
  13.  
  14. public class ServerHandle extends ChannelInboundHandlerAdapter {
  15.  
  16. private Server server;
  17.  
  18. public ServerHandle(Server server) {
  19. // TODO Auto-generated constructor stub
  20. this.server = server;
  21. }
  22. /**
  23. * 读写超时事事件
  24. */
  25. @Override
  26. public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
  27. if(evt instanceof IdleStateEvent) {
  28. IdleStateEvent event=(IdleStateEvent)evt;
  29. //如果读超时
  30. if(event.state()==IdleState.READER_IDLE) {
  31. System.out.println("有客户端超时了");
  32. ctx.channel().close();//关闭连接
  33. }
  34. }else {
  35. super.userEventTriggered(ctx, evt);
  36. }
  37.  
  38. }
  39.  
  40. // 建立连接时回调
  41. @Override
  42. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  43. // TODO Auto-generated method stub
  44. System.out.println("有客户端建立连接了");
  45. server.addClient(ctx);
  46. // ctx.fireChannelActive();//pipeline可以注册多个handle 这里可以理解为是否通知下一个Handle继续处理
  47. }
  48.  
  49. // 接收到客户端发送消息时回调
  50. @Override
  51. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  52. Message message=(Message)msg;
  53. if(message.getHead().getType().equals("ping")) {
  54. //表示心跳包 服务端响应心跳包 而不做相关业务处理
  55. MessageHead head=new MessageHead();
  56. byte[] content="".getBytes();
  57. head.setCreateDate(new Date());
  58. head.setType("ping");
  59. head.setLength(content.length);
  60. Message pingMessage=new Message(head,content);
  61. head.setToken(pingMessage.buidToken());
  62. ctx.writeAndFlush(pingMessage);
  63. }else {
  64. System.out.println("server接收到客户端发送信息:" + msg.toString());
  65. }
  66. // TODO Auto-generated method stub
  67.  
  68. // ctx.fireChannelRead(msg);pipeline可以注册多个handle 这里可以理解为是否通知下一个Handle继续处理
  69. }
  70.  
  71. // 通信过程中发生异常回调
  72. @Override
  73. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  74. // TODO Auto-generated method stub
  75. // super.exceptionCaught(ctx, cause);
  76. ctx.close();// 发生异常关闭通信通道
  77. System.out.println("发生异常与客户端失去连接");
  78.  
  79. cause.printStackTrace();
  80. // ctx.fireExceptionCaught(cause);pipeline可以注册多个handle 这里可以理解为是否通知下一个Handle继续处理
  81. }
  82. }

client

  1. package com.liqiang.nettyTest2;
  2.  
  3. import com.liqiang.SimpeEcode.Message;
  4.  
  5. import io.netty.bootstrap.Bootstrap;
  6. import io.netty.channel.ChannelFuture;
  7. import io.netty.channel.ChannelHandlerContext;
  8. import io.netty.channel.ChannelInitializer;
  9. import io.netty.channel.ChannelOption;
  10. import io.netty.channel.EventLoopGroup;
  11. import io.netty.channel.nio.NioEventLoopGroup;
  12. import io.netty.channel.socket.SocketChannel;
  13. import io.netty.channel.socket.nio.NioSocketChannel;
  14. import io.netty.handler.codec.string.StringDecoder;
  15. import io.netty.handler.codec.string.StringEncoder;
  16. import io.netty.util.concurrent.EventExecutorGroup;
  17.  
  18. public class Client implements Runnable{
  19. private String ip;// ip
  20. private int port;// 端口
  21. private boolean isConnection = false;
  22. private ChannelHandlerContext serverChannel;
  23.  
  24. public Client(String ip, int port) {
  25. this.ip = ip;
  26. this.port = port;
  27. }
  28.  
  29. // 与服务器建立连接
  30. public void connection() {
  31. new Thread(this).start();
  32.  
  33. }
  34. @Override
  35. public void run() {
  36. // TODO Auto-generated method stub
  37. EventLoopGroup group = new NioEventLoopGroup();// 服务器监听服务器发送信息
  38. Bootstrap bootstrap = new Bootstrap();
  39. bootstrap.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
  40. .handler(new ClientChannelInitializer(this));// 基于NIO编程模型通信
  41. try {
  42. ChannelFuture channelFuture = bootstrap.connect(ip, port).sync();
  43.  
  44. channelFuture.channel().closeFuture().sync();
  45. } catch (InterruptedException e) {
  46. // TODO Auto-generated catch block
  47. System.out.println("连接服务器失败");
  48. }finally {
  49. //尝试重连
  50. System.out.println("正在重连");
  51. run();
  52. }
  53. }
  54.  
  55. public void close() {
  56. serverChannel.close();
  57. }
  58. public boolean isConnection() {
  59. return isConnection;
  60. }
  61.  
  62. public void setConnection(boolean isConnection) {
  63. this.isConnection = isConnection;
  64. }
  65.  
  66. public void sendMsg(Message msg) {
  67. while(isConnection) {
  68. serverChannel.writeAndFlush(msg);
  69. }
  70.  
  71. }
  72.  
  73. public ChannelHandlerContext getServerChannel() {
  74. return serverChannel;
  75. }
  76.  
  77. public void setServerChannel(ChannelHandlerContext serverChannel) {
  78. this.serverChannel = serverChannel;
  79. }
  80.  
  81. }

Server

  1. package com.liqiang.nettyTest2;
  2.  
  3. import java.net.InetSocketAddress;
  4. import java.util.List;
  5. import java.util.Vector;
  6.  
  7. import com.liqiang.SimpeEcode.Message;
  8.  
  9. import io.netty.bootstrap.Bootstrap;
  10. import io.netty.bootstrap.ServerBootstrap;
  11. import io.netty.channel.ChannelFuture;
  12. import io.netty.channel.ChannelHandlerContext;
  13. import io.netty.channel.ChannelInitializer;
  14. import io.netty.channel.ChannelOption;
  15. import io.netty.channel.EventLoopGroup;
  16. import io.netty.channel.nio.NioEventLoopGroup;
  17. import io.netty.channel.socket.SocketChannel;
  18. import io.netty.channel.socket.nio.NioServerSocketChannel;
  19. import io.netty.channel.socket.nio.NioSocketChannel;
  20. import io.netty.handler.codec.string.StringDecoder;
  21. import io.netty.handler.codec.string.StringEncoder;
  22.  
  23. public class Server implements Runnable {
  24. private int port;// 监听端口
  25. private Vector<ChannelHandlerContext> clients;// 保存在线客户端信息
  26.  
  27. public Server(int port) {
  28. clients = new Vector<ChannelHandlerContext>();
  29. this.port = port;
  30. }
  31.  
  32. // 广播
  33. public void sendAll(Message msg) {
  34. clients.forEach(c -> {
  35. c.writeAndFlush(msg);
  36. });
  37. }
  38.  
  39. public void addClient(ChannelHandlerContext client) {
  40. clients.add(client);
  41. }
  42.  
  43. @Override
  44. public void run() {
  45. /**
  46. * NioEventLoopGroup 内部维护一个线程池 如果构造函数没有指定线程池数量 则默认为系统core*2
  47. */
  48. EventLoopGroup acceptor = new NioEventLoopGroup();// acceptor负责监客户端连接请求
  49. EventLoopGroup worker = new NioEventLoopGroup();// worker负责io读写(监听注册channel的 read/writer事件)
  50.  
  51. ServerBootstrap bootstrap = new ServerBootstrap();
  52. bootstrap.group(acceptor, worker).channel(NioServerSocketChannel.class)
  53. .localAddress(new InetSocketAddress(port)).childHandler(new ServerChannelInitializer(this))
  54. .option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);
  55. try {
  56. ChannelFuture channelFuture = bootstrap.bind(port).sync();
  57.  
  58. System.out.println("服务器已启动");
  59. // 将阻塞 直到服务器端关闭或者手动调用
  60. channelFuture.channel().closeFuture().sync();
  61. // 释放资源
  62.  
  63. } catch (InterruptedException e) {
  64. // TODO Auto-generated catch block
  65. e.printStackTrace();
  66. }finally {
  67. acceptor.shutdownGracefully();
  68. worker.shutdownGracefully();
  69. }
  70.  
  71. }
  72.  
  73. public void startServer() {
  74. new Thread(this).start();
  75. }
  76.  
  77. }

测试

  1. package com.liqiang.nettyTest2;
  2.  
  3. import java.util.Date;
  4.  
  5. import com.liqiang.SimpeEcode.Message;
  6. import com.liqiang.SimpeEcode.MessageHead;
  7.  
  8. public class nettyClientMain {
  9. public static void main(String[] args) {
  10. new Thread(new Runnable() {
  11.  
  12. @Override
  13. public void run() {
  14. // TODO Auto-generated method stub
  15. Client client1 = new Client("127.0.0.1", 8081);
  16. client1.connection();
  17. String content = "哈哈哈哈!";
  18. byte[] bts = content.getBytes();
  19. MessageHead head = new MessageHead();
  20. // 令牌生成时间
  21. head.setCreateDate(new Date());
  22. head.setType("message");
  23. head.setLength(bts.length);
  24. Message message = new Message(head, bts);
  25. message.getHead().setToken(message.buidToken());
  26. client1.sendMsg(message);
  27.  
  28. }
  29. }).start();
  30.  
  31. }
  32. }
  1. package com.liqiang.nettyTest2;
  2.  
  3. import java.text.DateFormat;
  4. import java.text.SimpleDateFormat;
  5. import java.util.Date;
  6.  
  7. import javax.management.StringValueExp;
  8. import javax.swing.text.StringContent;
  9.  
  10. import com.liqiang.SimpeEcode.Message;
  11. import com.liqiang.SimpeEcode.MessageHead;
  12.  
  13. public class nettyMain {
  14. public static void main(String[] args) {
  15. new Thread(new Runnable() {
  16.  
  17. @Override
  18. public void run() {
  19. // TODO Auto-generated method stub
  20. Server server = new Server(8081);
  21. server.startServer();
  22.  
  23. }
  24. }).start();
  25.  
  26. }
  27.  
  28. }

1.先开启服务端

2.再开启客户端

3.关闭服务端

然后我们再重新启动服务端 打印

  1. 正在重连
  2. 正在重连
  3. 正在重连
  4. 正在重连
  5. 正在重连
  6. 正在重连
  7. 正在重连
  8. 正在重连
  9. 正在向服务端发送心跳包
  10. 正在向服务端发送心跳包
  11. 正在向服务端发送心跳包
  12. 正在向服务端发送心跳包

netty心跳机制和断线重连(四)的更多相关文章

  1. Netty — 心跳检测和断线重连

    一.前言 由于在通信层的网络连接的不可靠性,比如:网络闪断,网络抖动等,经常会出现连接断开.这样对于使用长连接的应用而言,当突然高流量冲击势必会造成进行网络连接,从而产生网络堵塞,应用响应速度下降,延 ...

  2. Netty学习篇④-心跳机制及断线重连

    心跳检测 前言 客户端和服务端的连接属于socket连接,也属于长连接,往往会存在客户端在连接了服务端之后就没有任何操作了,但还是占用了一个连接:当越来越多类似的客户端出现就会浪费很多连接,netty ...

  3. Netty 如何实现心跳机制与断线重连?

    作者:sprinkle_liz www.jianshu.com/p/1a28e48edd92 心跳机制 何为心跳 所谓心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, ...

  4. NETTY 心跳机制

    最近工作比较忙,但闲暇之余还是看了阿里的冯家春(fengjiachun)的github上的开源代码Jupiter,写的RPC框架让我感叹人外有人,废话不多说,下面的代码全部截取自Jupiter,写了一 ...

  5. Netty心跳机制

    一.概念介绍网络中的接收和发送数据都是使用操作系统中的SOCKET进行实现.但是如果此套接字已经断开,那发送数据和接收数据的时候就一定会有问题.可是如何判断这个套接字是否还可以使用呢?这个就需要在系统 ...

  6. netty心跳机制测试

    netty中有比较完善的心跳机制,(在基础server版本基础上[netty基础--基本收发])添加少量代码即可实现对心跳的监测和处理. 1 server端channel中加入心跳处理机制 // Id ...

  7. 连接管理 与 Netty 心跳机制

    一.前言 踏踏实实,动手去做,talk is cheap, show me the code.先介绍下基础知识,然后做个心跳机制的Demo. 二.连接 长连接:在整个通讯过程,客户端和服务端只用一个S ...

  8. webSocket使用心跳包实现断线重连

    首先new一个webscoket的连接 let noticeSocketLink = new WebSocket(‘webSocket的地址’) 这里是连接成功之后的操作 linkNoticeWebs ...

  9. netty心跳机制解决

    直接看别个的源码:https://blog.csdn.net/xt8469/article/details/84827443>>https://blog.csdn.net/xt8469/a ...

随机推荐

  1. jQery总结01

    1 jQuery 的基本语法结构是什么? 2 $(document).ready() 与 window.onload 有什么区别? 3 如何实现 DOM 对象和 jQuery对象间的转化?

  2. GitHub上排名前100的Android开源库介绍

    GitHub上排名前100的Android开源库介绍 文章来源: http://www.open-open.com/news/view/1587067#6734290-qzone-1-31660-bf ...

  3. 520D

    模拟 很明显应该尽量选最大或最小的数.那么我们维护一个set,再维护一个mp,每次检查是否能选,如果选完这个数上面的东西不悬空就可以选,每次选完都要更新四周-2+2的方块,因为再远就影响不到了 #in ...

  4. 88. [ExtJS2.1教程-5]ToolBar(工具栏)

    转自:https://llying.iteye.com/blog/324681 面板中可以有工具栏,工具栏可以位于面板顶部或底部,Ext中工具栏是由Ext.Toolbar类来表示.工具栏上可以放按钮. ...

  5. Balloons(DFS)

    http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2248 题意:(1)求图中四连块(有公共边的方块 ...

  6. javascript从作用域链的角度看闭包

    闭包 闭包是一个能访问外部函数定义的变量的函数. 为什么? 当访问一个变量时,解释器会首先在当前作用域查找标示符,如果没有找到,就去父作用域找,直到找到该变量的标示符或者不再存在父作用域了,这就是作用 ...

  7. Azure Command Line(Azure CLI)指南

    1.安装. MSI安装程序:https://aka.ms/installazurecliwindows https://docs.microsoft.com/zh-cn/cli/azure/insta ...

  8. Java NIO Buffer说明

    Buffer 有3个重要的参数:位置(position).容量(capactiy).上限(limit) 位置(position): 写:当前缓冲区的位置,将从position的下一个位置写数据. 读: ...

  9. BZOJ 1975 k短路 A*

    思路: 直接上A* //By SiriusRen #include <queue> #include <cstdio> #include <cstring> #in ...

  10. A - Diverse Team

    Problem description There are n students in a school class, the rating of the i-th student on Codeho ...