SocketChannel类可以读写TCP Socket。数据的读写必须以ByteBuffer的形式。

连接

SocketChannel提供了静态的open方法来创建新的SocketChannel对象:

  1. public static SocketChannel open() throws IOException {
  2. return SelectorProvider.provider().openSocketChannel();
  3. }
  1. public static SocketChannel open(SocketAddress remote)
  2. throws IOException
  3. {
  4. SocketChannel sc = open();
  5. try {
  6. sc.connect(remote);
  7. } catch (Throwable x) {
  8. try {
  9. sc.close();
  10. } catch (Throwable suppressed) {
  11. x.addSuppressed(suppressed);
  12. }
  13. throw x;
  14. }
  15. assert sc.isConnected();
  16. return sc;
  17. }

这是两个open方法,第一个方法只会创建一个Channel但是不会进行连接,之后必须用connect方法来进行连接。第二个方法接收一个SocketAddress用来指定要连接的机器和端口号,它会进行连接,因此会阻塞,在连接成功建立或者抛出异常之前,这个方法不会返回。

以非阻塞方式建立连接

当我们需要对socket的选项进行配置或者对通道进行配置时,最好选择上面说的第一种方式。特别是当我们需要以非阻塞方式来打开通道时:

  1. public static void main(String[] args) throws IOException {
  2. SocketChannel socketChannel = SocketChannel.open();
  3. SocketAddress socketAddress = new InetSocketAddress("www.cafeaulait.org", 80);
  4. socketChannel.configureBlocking(false);
  5. socketChannel.connect(socketAddress);
  6. }

configureBlocking方法用来设置是否阻塞。当设置为非阻塞时,connect方法会立即返回,甚至在连接建立之前就返回。

使用finishConnect方法

在非阻塞模式下,在使用连接之前,我们必须调用finishConnect()方法来完成连接。这个方法只对非阻塞模式是必需的,如果连接现在可用,finishConnect会返回true,如果连接还没建立,会返回false,如果连接无法建立,会抛出异常。如果当前是阻塞模式,那么这个方法就会被阻塞,直到连接成功或者失败,这时候就要么返回true要么抛出异常。

获取连接的状态

我们可以调用isConnected和isConnectionPending方法来获取连接是否成功或者还未完成。

  1. /**
  2. * Tells whether or not this channel's network socket is connected.
  3. *
  4. * @return <tt>true</tt> if, and only if, this channel's network socket
  5. * is {@link #isOpen open} and connected
  6. */
  7. public abstract boolean isConnected();
  8. /**
  9. * Tells whether or not a connection operation is in progress on this
  10. * channel.
  11. *
  12. * @return <tt>true</tt> if, and only if, a connection operation has been
  13. * initiated on this channel but not yet completed by invoking the
  14. * {@link #finishConnect finishConnect} method
  15. */
  16. public abstract boolean isConnectionPending();

写入数据

channel连接建立成功之后,我们就可以向通道写入或者从通道读取数据了,有以下方法可以完成读写操作:
image.png

读取到单个ByteBuffer

我们先来看read方法:

  1. /**
  2. * @throws NotYetConnectedException
  3. * If this channel is not yet connected
  4. */
  5. public abstract int read(ByteBuffer dst) throws IOException;

这个方法将读取的数据写入dst中。

读取到多个ByteBuffer

后面的两个方法读取channel中的数据到一个ByteBuffer数组:

  1. public abstract long read(ByteBuffer[] dsts, int offset, int length)
  2. throws IOException;
  3. /**
  4. * @throws NotYetConnectedException
  5. * If this channel is not yet connected
  6. */
  7. public final long read(ByteBuffer[] dsts) throws IOException {
  8. return read(dsts, 0, dsts.length);
  9. }

写入数据

写入数据与读取数据一样,也支持从一个缓冲区写入和从多个缓冲区写入:

  1. /**
  2. * @throws NotYetConnectedException
  3. * If this channel is not yet connected
  4. */
  5. public abstract int write(ByteBuffer src) throws IOException;
  6. /**
  7. * @throws NotYetConnectedException
  8. * If this channel is not yet connected
  9. */
  10. public abstract long write(ByteBuffer[] srcs, int offset, int length)
  11. throws IOException;
  12. /**
  13. * @throws NotYetConnectedException
  14. * If this channel is not yet connected
  15. */
  16. public final long write(ByteBuffer[] srcs) throws IOException {
  17. return write(srcs, 0, srcs.length);
  18. }

关闭channel

我们可以直接使用close()方法来关闭channel。