NIO之套接字通道

使用过 Unix/Linux 系统下的 socket 接口,那么对 socket 编程的过程应该有一些了解。对于 TCP 服务端,接口调用的顺序为socket() -> bind() -> listen() -> accept() -> 其他操作 -> close(),客户端的顺序为socket() -> connect() -> 其他操作 -> close()。如下图所示:

Java 套接字通道包含三种类型,分别是

类型说明
DatagramChannel UDP 网络套接字通道
SocketChannel TCP 网络套接字通道
ServerSocketChannel TCP 服务端套接字通道

打开通道

SocketChannel 和 ServerSocketChannel 是抽象类,所以不能直接通过构造方法创建通道。这两个类均是使用 open 方法创建通道,如下:

SocketChannel socketChannel = SocketChannel.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

关闭通道

SocketChannel 和 ServerSocketChannel 均提供了 close 方法,用于关闭通道。示例如下:

SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("www.coolblog.xyz", 80));
// do something...
socketChannel.close();

/*******************************************************************/

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(8080));
SocketChannel socketChannel = serverSocketChannel.accept();
// do something...
socketChannel.close();
serverSocketChannel.close();

读操作

通过使用 SocketChannel 的 read 方法,并配合 ByteBuffer 字节缓冲区,即可以从 SocketChannel 中读取数据。示例如下:

ByteBuffer buffer = ByteBuffer.allocate(32);
int num = socketChannel.read(buffer);

写操作

读取数据使用的是 read 方法,那么写入自然也就是 write 方法了。NIO 通道是面向缓冲的,所以向管道中写入数据也需要和缓冲区配合才行。示例如下

String data = "Test data..."

ByteBuffer buffer = ByteBuffer.allocate(32);
buffer.clear();
buffer.put(data.getBytes());

bbuffer.flip();
channel.write(buffer);

非阻塞模式

与文件通道不同,套接字通道可以运行在非阻塞模式下。在此模式下,调用 connect(),read() 和 write() 等方法时,进程/线程会立即返回。设置非阻塞模式的方法为configureBlocking,我们来看一下该方法的使用示例:

SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("www.coolblog.xyz", 80));

// 这里要循环检测是否已经连接上
while(!socketChannel.finishConnect()){
    // do something
}

// 连接建立起来后,才能进行读取或写入操作

由于在非阻塞模式下,调用 connect 方法会立即返回。如果在连接未建立起来的情况下,从管道中读取,或向管道写入数据,会触发 NotYetConnectedException 异常。所以要进行循环检测,以保证连接完成建立。如果代码按照上面那样去写,会引发另外一个问题。非阻塞模式虽然不会阻塞线程,但是在方法返回后,还要进行循环检测,线程实际上还是被阻塞。出现这个问题的原因是和 Java NIO 套接字通道的 IO 模型有关,套接字通道采用的是“同步非阻塞”式 IO 模型,用户发起一个 IO 操作后,即可去做其他事情,不用等待 IO 完成。但是 IO 是否已完成,则需要用户自己时不时的去检测,这样实际上还是会浪费 CPU 资源。

 

参考:

http://www.tianxiaobo.com/2018/03/25/Java-NIO%E4%B9%8B%E5%A5%97%E6%8E%A5%E5%AD%97%E9%80%9A%E9%81%93/

posted on 2018-10-22 15:58  溪水静幽  阅读(196)  评论(0)    收藏  举报