Channel
提供了对网络连接的抽象,通过它你可以执行基本的读写操作,比如读取数据、写入数据、绑定端口等。它还负责触发各种I/O事件,比如连接建立、关闭、读取就绪等。
Channel接口的继承实现关系图
关注一下SelectableChannel
这个接口,它代表了一个可以注册到Selector
的通道。这个接口的设计是为了支持多路复用I/O操作,即在一个单独的线程中监控多个通道的I/O活动。
SelectableChannel的主要特点:
- 选择性:实现
SelectableChannel
接口的通道可以注册到Selector
上,从而可以在单个线程中同时监控多个这样的通道。 - 阻塞/非阻塞模式:提供了
configureBlocking(boolean)
方法来控制该通道是否以阻塞还是非阻塞模式工作。 - 选择键:每个注册到
Selector
上的SelectableChannel
都会关联一个SelectionKey
,这个健包含了有关该通道的信息,比如感兴趣的I/O操作类型以及是否准备好进行这些操作。
这里不全部展开,只对FileChannel
、SocketChannel
、ServerSocketChannel
、DatagramChannel
四种重要的实现做介绍,其中SocketChannel
、ServerSocketChannel
、DatagramChannel
这三个实现了SelectableChannel
接口。
- FileChannel:文件通道,用于文件读写数据。
- SocketChannel:socket套接字通道,用于客户端套接字TCP连接的数据读写。
- ServerSocketChannel:服务器套接字通道,允许我们监听TCP连接,为每个监听到的请求创建一个SocketChannel通道。
- DatagramChannel:UDP连接时的数据报通道。
FileChannel
文件通道,顾名思义,专门操作磁盘文件的通道。需要注意的地方,FileChannel为阻塞模式,不能设置为非阻塞模式。
-
获取FileChannel
// 获取一个文件输入流channel FileInputStream fis = new FileInputStream(srcFile); FileChannel inChannel = fis.getChannel(); FileOutputStream fos = new FileOutputStream(destFile); FileChannel outChannel = fos.getChannel(); // 通过RandomAccessFile(文件随机访问)类来获取FileChannel RandomAccessFile rFile = new RandomAccessFile("file.txt", "rw"); FileChannel channel = rFile.getChannel();
-
读取FileChannel
RandomAccessFile file = new RandomAccessFile("file.txt", "rw"); FileChannel channel = file.getChannel(); ByteBuffer buf = ByteBuffer.allocate(1024); int length = -1; while ((length = channel.read(buf)) != -1) { // handle buf }
-
写入FileChannel
// channel写模式下,对于Buffer来说就是读模式,buf就需要翻转一下 buf.flip(); int outLength = 0; while ((outLength = outChannel.write(buf)) != 0) { System.out.println("写入字节数:" + outLength); }
-
关闭通道
channel.close();
-
强制刷新到磁盘
channel.force(true);
ServerSocketChennel
和SocketChannel
在Java NIO中,涉及到网络连接的通道有两个:一个是SocketChannel,负责连接的数据传输;另一个是ServerSocketChannel,负责连接的监听。无论是ServerSocketChannel还是SocketChannel,都支持阻塞和非阻塞两种模式。
socketChannel.configureBlocking(false);
socketChannel.configureBlocking(true);
-
获取SocketChannel传输通道
// 获取一个套接字通道 SocketChannel socketChannel = SocketChannel.open(); // 设置为非阻塞模式 socketChannel.configureBlocking(false); // 对服务器的IP和端口发起连接 socketChannel.connect(new InetSocketAddress("127.0.0.1", 80)); while (!socketChannel.finishConnect()) { // 在连接期间做一些其它事 } // 服务端 ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel socketChannel = server.accept(); socketChannel.configureBlocking(false);
-
读取SocketChannel
ByteBuffer buf = ByteBuffer.allocate(1024); int bytesRead = socketChannel.read(buf);
-
写入SocketChannel
buffer.flip(); socketChannel.write(buffer);
-
关闭SocketChannel
socketChannel.shutdownOutput(); IOUtil.closeQuietly(socketChannel);
DatagramChannel
DatagramChannel用来处理UDP协议传输协议。
-
获取DatagramChannel
DatagramChannel channel = DatagramChannel.open(); channel.configureBlocking(false); channel.socket().bind(new InetSocketAddress(18080));
-
读取DatagramChannel
ByteBuffer buf = ByteBuffer.allocate(1024); SocketAddress clientAddr = datagramChannel.receive(buf);
-
写入DatagramChannel
buffer.flip(); channel.send(buffer, new InetSocketAddress("127.0.0.1", 18899)); buffer.clear();
-
关闭DatagramChannel
channel.close();