Channel
通道,被建立的一个应用程序和操作系统交互事件、传递内容的渠道(注意是连接到操作系统)。那么既然是和操作系统进行内容的传递,那么说明应用程序可以通过通道读取数据,也可以通过通道向操作系统写数据,而且可以同时进行读写。
- NIO 的通道类似于流,但有些区别如下:
- **通道可以同时进行读写**,而流只能读或者只能写- 通道可以实现异步读写数据- 通道可以从缓冲读数据,也可以写数据到缓冲
- 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
/*** Scattering:将数据写入到buffer时,可以采用buffer数组,依次写入 [分散]* Gathering: 从buffer读取数据时,可以采用buffer数组,依次读*/public class ScatteringAndGatheringTest {public static void main(String[] args) throws Exception {//使用 ServerSocketChannel 和 SocketChannel 网络ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();InetSocketAddress inetSocketAddress = new InetSocketAddress(7000);//绑定端口到socket ,并启动serverSocketChannel.socket().bind(inetSocketAddress);//创建buffer数组ByteBuffer[] byteBuffers = new ByteBuffer[2];byteBuffers[0] = ByteBuffer.allocate(5);byteBuffers[1] = ByteBuffer.allocate(3);//等客户端连接(telnet)SocketChannel socketChannel = serverSocketChannel.accept();int messageLength = 8; //假定从客户端接收8个字节//循环的读取while (true) {int byteRead = 0;while (byteRead < messageLength ) {long l = socketChannel.read(byteBuffers);byteRead += l; //累计读取的字节数System.out.println("byteRead=" + byteRead);//使用流打印, 看看当前的这个buffer的position 和 limitArrays.asList(byteBuffers).stream().map(buffer -> "postion=" + buffer.position() + ", limit=" + buffer.limit()).forEach(System.out::println);}//将所有的buffer进行flipArrays.asList(byteBuffers).forEach(buffer -> buffer.flip());//将数据读出显示到客户端long byteWirte = 0;while (byteWirte < messageLength) {long l = socketChannel.write(byteBuffers); //byteWirte += l;}//将所有的buffer 进行clearArrays.asList(byteBuffers).forEach(buffer-> {buffer.clear();});System.out.println("byteRead:=" + byteRead + " byteWrite=" + byteWirte + ", messagelength" + messageLength);}}}
