public class NioServer {
  public static void main(String[] args) {

    ServerSocketChannel serverSocketChannel;
    Selector selector;
    try {
      serverSocketChannel = ServerSocketChannel.open();
      serverSocketChannel.bind(new InetSocketAddress(2222));
      serverSocketChannel.configureBlocking(false);
      selector = Selector.open();
      serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    } catch (IOException e) {
      e.printStackTrace();
      return;
    }

    while (true) {
      try {
        selector.select(); // 设置为非阻塞,无输数据则selectedKeys为空
      } catch (IOException e) {
        e.printStackTrace();
        break;
      }

      Set<SelectionKey> readyKeys = selector.selectedKeys();
      Iterator<SelectionKey> iterator = readyKeys.iterator();
      while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        iterator.remove();
        try {
          if (key.isAcceptable()) {
            ServerSocketChannel server = (ServerSocketChannel) key.channel();
            SocketChannel client = server.accept();
            client.configureBlocking(false);
            SelectionKey key2 = client.register(selector, SelectionKey.OP_WRITE);
            ByteBuffer buffer = ByteBuffer.allocate(74);
            buffer.put(new Date().toString().getBytes());
            buffer.put((byte) '\r');
            buffer.put((byte) '\n');
            buffer.flip();
            key2.attach(buffer);
          } else if (key.isWritable()) {
            SocketChannel client = (SocketChannel) key.channel();
            ByteBuffer buffer = (ByteBuffer) key.attachment();
            //if (!buffer.hasRemaining()) { // 判断是否还有空间 position到limit
              buffer.rewind();
              buffer.put(new Date().toString().getBytes());
              buffer.put((byte) '\r');
              buffer.put((byte) '\n');
              buffer.flip();
          //  }
            client.write(buffer);
          }
        } catch (IOException e) {
          key.cancel();
          try {
            key.channel().close();
          } catch (IOException e1) {
            e1.printStackTrace();
          }
        }
      }
    }

  }
}

备注:

  • clear()清空缓冲区,位置设为0,限制设置为容量大小
    侧重于还原一切状态,通常为一系列新的通道读取或写入操作做好准备
  • flip() limit设置为当前position,position设置为0
    侧重于substring()截取
  • rewind()position设置为0
    侧重于重新,重新读取、重新写入。
public class NioClient {

  public static void main(String[] args) {
    SocketAddress address = new InetSocketAddress("localhost", 2222);
    try {
      SocketChannel client = SocketChannel.open(address);
      client.configureBlocking(false); // true阻塞,false不阻塞
      ByteBuffer buffer = ByteBuffer.allocate(74); // 申请字节大小
      WritableByteChannel out = Channels.newChannel(System.out);

      while (true) {
        int n = client.read(buffer);// 在非阻塞模式下,read()可能因为读不到任何数据而返回0,阻塞模式下就会一直阻塞到有数据
        if (n > 0) {
          buffer.flip(); // 翻转
          out.write(buffer);
          buffer.clear(); // 清空缓存区
        } else if (n == -1) {
          // 不会发生
          break;
        }
      }


    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

依次启动服务端,客户端,客户端收到当前时间消息。

确认同步,阻塞的概念:网上说的乱七八糟:

同步不同步取决于一次可以处理几个请求,阻塞不阻塞取决于服务端处理客户端请求,客户端处理服务端返回数据是否有阻塞可能

  • bio:同步阻塞,一次只能处理一个请求

传统SocketServer.accept()阻塞直到有新的客户端接入,在当前对客户
端的处理工作未同步完成前,不能完全其它工作。Socket.getInputStream()阻塞直到有数据流。

  • nio:同步阻塞/同步非阻塞,可以配置非阻塞模式,一次只能处理一个请求

可以通过设置configureBlocking来决定是否为阻塞模式,非阻塞情况下,客户端SocketChannel.read()读不到数据则返回0。服务端selector.select()无数据,则selectedKey为空Set,ServerSocketChannel.accept()无客户端连接,立即返回null。

  • aio:异步非阻塞,一次可以处理多个请求

参考书籍《Java网络编程(第四版)》《Nio与Socket编程技术指南》


花溪的小石头
180 声望8 粉丝

境由心造,一切只在当下的转念之间。