SocketChannel 是连接到 TCP 网络套接字的 Channel,相当于 Java 网络编程中的 Socket。有两种创建 SocketChannel 的方式:
- 手动开启一个 SocketChannel 并连接到因特网上的一个服务器
- 当传入的连接到达 ServerSocketChannel 时或者说 ServerSocketChannel 新进来一个连接的时候会创建一个 SocketChannel
1、开启一个 SocketChannel
示例如下:
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));
2、关闭一个 SocketChannel
通过调用 SocketChannel 的 close() 方法可以关闭这个 SocketChannel:
socketChannel.close();
3、从 SocketChannel 中读取数据
可以通过调用 SocketChannel 的 read() 或其重载方法读取 SocketChannel 中的数据:
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = socketChannel.read(buf);
首先分配一个 Buffer,数据从 SocketChannel 中读取后写入该 Buffer。然后调用 SocketChannel.read() 方法达到上述目的,该方法的返回值显示了向指定的 Buffer 中写入了多少个字节的数据,如果返回 -1 表示数据读取结束(连接被关闭)。
4、向 SocketChannel 中写数据
调用 SocketChannel 的 write() 方法可以向 SocketChannel 中写数据,需要传入一个 Buffer,表示将该 Buffer 中的数据写入到 SocketChannel 中:
String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();//从头开始写入
while(buf.hasRemaining()) {
channel.write(buf);//将Buffer中的数据写入到SocketChannel中
}
注意把 SocketChannel.write() 方法的调用放在一个 while 循环中。由于无法保证有多少字节会写入到 SocketChannel 中,因此需要重复调用 write() 方法直到没有数据可以写为止。
5、非阻塞模式
可以将 SocketChannel 设置为非阻塞的。这样的话,就可以使 connect()、read()、write() 这些方法异步执行。
①connect()
如果 SocketChannel 设置为了非阻塞的,在调用 connect() 方法的时候,这个方法可能会在连接建立之前返回。要确定连接是否已经建立,可以调用 finishConnect() 方法,示例如下:
socketChannel.configureBlocking(false);//将SocketChannel设置为非阻塞的
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));//建立连接(异步)
while(!socketChannel.finishConnect() ){//若连接尚未创建
//等待,或者执行别的操作
}
②write()
非阻塞模式下 write() 方法可能在还没有写入任何数据时返回。因此需要在一个循环中调用 write(),我们前面的示例中一直是这么做的,因此对于 write() 来说不需要做什么改动。
③read()
在非阻塞模式下 read() 方法可能会在还没有读取任何数据的情况下返回。因此需要关注该方法的返回值,该返回值表明了读取了多少字节的数据。
④非阻塞模式下的 Selector
非阻塞模式的 SocketChannel 和 Selector 搭配更好,将一个或多个 SocketChannel 注册到一个 Selector 中,然后通过轮询这个 Selector 查看是否有准备好读写的 Channel。