zoukankan      html  css  js  c++  java
  • NIO实践-心跳检测

      今天将NIO实现简版心跳检测功能做一下笔记,旨在加深理解NIO客户端与服务端交互的状态监听,以及固定的编码套路。其实跟产品级的心跳检测(包括但不限于token验证、服务性能参数获取等)尚且存在差距。暂且忽略。

    一、方案设计

     二、服务端代码实现

     1 public class HeartBeatServer {
     2     @Test
     3     public void Test() throws IOException {
     4         // 构建服务端Socket
     5         ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
     6         // 绑定端口
     7         serverSocketChannel.bind(new InetSocketAddress(8080));
     8         // 设置为异步Socket
     9         serverSocketChannel.configureBlocking(false);
    10         // 建立服务端多路复用器
    11         Selector selector = Selector.open();
    12         // 服务端Socket注册接入监听
    13         serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    14         try {
    15             dispatch(selector, serverSocketChannel);
    16         } catch (Exception e) {
    17             e.printStackTrace();
    18         }
    19     }
    20     private void dispatch(Selector selector, ServerSocketChannel serverSocketChannel) throws IOException {
    21         while (true) {
    22             selector.select();
    23             Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
    24             if (iterator.hasNext()) {
    25                 SelectionKey key = iterator.next();
    26                 iterator.remove();
    27                 if (!key.isValid()) {
    28                     continue;
    29                 }
    30                 if (key.isAcceptable()) {
    31                     // 服务端通过serverSocketChannel.accept()不断的处理新的客户端接入
    32                     SocketChannel socketChannel = serverSocketChannel.accept();
    33                     socketChannel.configureBlocking(false);
    34                     // 注册新接入的客户端channel为可读,即等待读取客户端发送心跳信息
    35                     socketChannel.register(selector, SelectionKey.OP_READ);
    36                 } else if (key.isReadable()) {
    37                     SocketChannel channel = (SocketChannel) key.channel();
    38                     ByteBuffer buffer = ByteBuffer.allocate(1024);
    39                     channel.read(buffer);
    40                     if (buffer.hasRemaining() && buffer.get(0) == 4) {
    41                         channel.close();
    42                         System.out.println("关闭管道:" + channel);
    43                         break;
    44                     }
    45                     System.out.println(new String(buffer.array(), 0, 20));
    46                     buffer.put(String.valueOf(System.currentTimeMillis()).getBytes());
    47                     buffer.flip();
    48                     channel.write(buffer);
    49                 }
    50             }
    51         }
    52     }
    53 }

    三、客户端代码

     1 public class HeartBeatClient1 {
     2     @Test
     3     public void Test() throws IOException, InterruptedException {
     4         // 初始化客户端Socket
     5         SocketChannel socketChannel = SocketChannel.open();
     6         socketChannel.configureBlocking(false);
     7         Selector selector = Selector.open();
     8         // 注册连接成功事件监听
     9         socketChannel.register(selector, SelectionKey.OP_CONNECT);
    10         // 发起连接
    11         socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
    12         while (true) {
    13             selector.select();
    14             Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
    15             if (iterator.hasNext()) {
    16                 SelectionKey key = iterator.next();
    17                 iterator.remove();;
    18                 if (!key.isValid()) {
    19                     continue;
    20                 }
    21                 // 当连接到服务端成功时,变更监听为 可写
    22                 if (key.isConnectable()) {
    23                     SocketChannel channel = (SocketChannel) key.channel();
    24                     channel.finishConnect();
    25                     key.interestOps(SelectionKey.OP_WRITE);
    26                 }
    27                 // 写入心跳信息,然后变更监听为 可读,即等待读取服务端 回执
    28                 if (key.isWritable()) {
    29                     SocketChannel channel = (SocketChannel) key.channel();
    30                     channel.write(ByteBuffer.wrap("heartBeatClient_1__".getBytes()));
    31                     key.interestOps(SelectionKey.OP_READ);
    32                 }
    33                 // 读取服务端回执后,然后变更监听为 可写,准备下一次写入心跳信息
    34                 if (key.isReadable()) {
    35                     SocketChannel channel = (SocketChannel) key.channel();
    36                     ByteBuffer buffer = ByteBuffer.allocate(64);
    37                     channel.read(buffer);
    38                     buffer.flip();
    39                     System.out.println(new String(buffer.array(), 0, buffer.limit()));
    40                     key.interestOps(SelectionKey.OP_WRITE);
    41                     Thread.sleep(2000);
    42                 }
    43             }
    44         }
    45     }
    46 }

    四、总结

      其实这个Demo虽然很糙,但是基本囊括了原生NIO编程的常规套路。总结如下:

      服务端:

        <1>、构建ServerSocketChannel。并设置为非阻塞。

        <2>、构建Selector。并注册服务端accept监听,来等待客户端接入事件。

        <3>、监听到客户端接入后,将客户端接入的SocketChannel注册到Selector,并监听该SocketChannel的Read事件,即等待读取客户端发送的数据。

        <4>、对于服务端来说,可能会有多个客户端接入,所以每次Selector轮询,都要从key里边取channel。

      客户端:

        <1>、构建客户端SockeChannel,并设置为非阻塞。

        <2>、构建Selector。并注册客户端connect监听。

        <3>、客户端发起connect操作,如果连接成功,则会触发<2>的监听。

        <4>、客户端自始至终都是只有一个channel,所以不用每次都从key里边取,即始终都是构建时的那个channel对象,另外,每次执行完操作都要考虑下一步要监听的操作。

      

  • 相关阅读:
    springboot事物和事物回滚
    MyBatis Sql语句中的转义字符
    使用 vagrant新建Linux虚拟机
    Centos 6.7 安装mongodb
    阿里云windows server 2012 TIME_WAIT CLOSE_WAIT
    使用Eclipse打jar包 包含依赖jar包
    linux crontab定时器
    mysql 存储过程学习笔记
    oracle windows 新建用户授权 导出导入bmp文件
    解决hash冲突的方法
  • 原文地址:https://www.cnblogs.com/UYGHYTYH/p/13336363.html
Copyright © 2011-2022 走看看