Channel

通道,被建立的一个应用程序和操作系统交互事件、传递内容的渠道(注意是连接到操作系统)。那么既然是和操作系统进行内容的传递,那么说明应用程序可以通过通道读取数据,也可以通过通道向操作系统写数据,而且可以同时进行读写。

  • NIO 的通道类似于流,但有些区别如下:
    1. - **通道可以同时进行读写**,而流只能读或者只能写
    2. - 通道可以实现异步读写数据
    3. - 通道可以从缓冲读数据,也可以写数据到缓冲
  • BIO 中的stream是单向的,例如 FileInputStream 对象只能进行读取数据的操作,而 NIO中的通道(Channel)是双向的,可以读操作,也可以写操作。
  • Channel 在 NIO 中是一个接口 public interface Channel extends Closeable{}
  • FileChannel 用于文件的数据读写
  • DatagramChannel 用于 UDP 的数据读写,ServerSocketChannel 和SocketChannel 用于 TCP 的数据读写
  • 服务器会创建一个ServerSocketChannel接受连接,连接来了之后,ServerSocketChannel会创建一个SocketChannel用来真正与客户端进行通信

ServerSocketChannel

应用服务器程序的监听通道。只有通过这个通道,应用程序才 能向操作系统注册支持“多路复用 IO”的端口监听。同时支持 UDP 协议和 TCP 协议。

SocketChannel

TCP Socket 套接字的监听通道,一个 Socket 套接字对应了一个客户 端 IP:端口 到 服务器 IP:端口的通信连接。

关于Buffer 和 Channel的注意事项和细节

  • ByteBuffer 支持类型化的 put 和 get, put 放入的是什么数据类型,get 就应该使用相应的数据类型来取出,否则可能有 BufferUnderflowException 异常。
  • 可以将一个普通 Buffer 转成只读 Buffer buffer.asReadOnlyBuffer()
  • NIO 还提供了 MappedByteBuffer, 可以让文件直接在内存(堆外的内存)中进行修改, 而如何同步到文件由 NIO 来完成
  • 前面我们讲的读写操作,都是通过一个 Buffer 完成的,NIO 还支持 通过多个 Buffer (即 Buffer 数组) 完成读写操作,即 Scattering 和Gathering

    1. /**
    2. * Scattering:将数据写入到buffer时,可以采用buffer数组,依次写入 [分散]
    3. * Gathering: 从buffer读取数据时,可以采用buffer数组,依次读
    4. */
    5. public class ScatteringAndGatheringTest {
    6. public static void main(String[] args) throws Exception {
    7. //使用 ServerSocketChannel 和 SocketChannel 网络
    8. ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    9. InetSocketAddress inetSocketAddress = new InetSocketAddress(7000);
    10. //绑定端口到socket ,并启动
    11. serverSocketChannel.socket().bind(inetSocketAddress);
    12. //创建buffer数组
    13. ByteBuffer[] byteBuffers = new ByteBuffer[2];
    14. byteBuffers[0] = ByteBuffer.allocate(5);
    15. byteBuffers[1] = ByteBuffer.allocate(3);
    16. //等客户端连接(telnet)
    17. SocketChannel socketChannel = serverSocketChannel.accept();
    18. int messageLength = 8; //假定从客户端接收8个字节
    19. //循环的读取
    20. while (true) {
    21. int byteRead = 0;
    22. while (byteRead < messageLength ) {
    23. long l = socketChannel.read(byteBuffers);
    24. byteRead += l; //累计读取的字节数
    25. System.out.println("byteRead=" + byteRead);
    26. //使用流打印, 看看当前的这个buffer的position 和 limit
    27. Arrays.asList(byteBuffers).stream().map(buffer -> "postion=" + buffer.position() + ", limit=" + buffer.limit()).forEach(System.out::println);
    28. }
    29. //将所有的buffer进行flip
    30. Arrays.asList(byteBuffers).forEach(buffer -> buffer.flip());
    31. //将数据读出显示到客户端
    32. long byteWirte = 0;
    33. while (byteWirte < messageLength) {
    34. long l = socketChannel.write(byteBuffers); //
    35. byteWirte += l;
    36. }
    37. //将所有的buffer 进行clear
    38. Arrays.asList(byteBuffers).forEach(buffer-> {
    39. buffer.clear();
    40. });
    41. System.out.println("byteRead:=" + byteRead + " byteWrite=" + byteWirte + ", messagelength" + messageLength);
    42. }
    43. }
    44. }