Java Nio 简明教程

Java NIO - Socket Channel

Java NIO 套接字通道是一种可选择类型通道,这意味着它可以使用选择器多路复用,用于面向流的数据流连接套接字。套接字通道可以通过调用其静态 open() 方法创建,前提是没有任何预先存在的套接字。套接字通道是通过调用 open 方法创建的,但尚未连接。为了连接套接字通道,需要调用 connect() 方法。这里需要注意的一点是,如果通道未连接并且试图尝试任何 I/O 操作,则此通道将抛出 NotYetConnectedException。因此,必须确保在执行任何 IO 操作之前通道已连接。一旦通道连接上,它将保持连接状态,直到它被关闭。可以通过调用其 isConnected 方法来确定套接字通道的状态。

套接字通道的连接可以通过调用其 finishConnect() 方法完成。是否正在进行连接操作可以通过调用 isConnectionPending 方法来确定。默认情况下,套接字通道支持非阻塞连接。它还支持异步关闭,这类似于通道类中指定的异步关闭操作。

套接字通道可以安全地供多个并发线程使用。它们支持并发读和写,尽管最多只有一个线程可以读取,并且最多只有一个线程可以在任何给定时间写入。connect 和 finishConnect 方法彼此互斥同步,并且在其中一个方法的调用正在进行时尝试启动读取或写入操作将阻塞,直到该调用完成。

Important methods of Socket channel

  1. bind(SocketAddress local) − 此方法用于将套接字通道绑定到作为此方法参数提供的本地地址。

  2. connect(SocketAddress remote) − 此方法用于将套接字连接到远程地址。

  3. finishConnect() − 此方法用于完成连接套接字通道的过程。

  4. getRemoteAddress() - 该方法返回通道 Socket 连接到的远程位置的地址。

  5. isConnected() - 如前所述,该方法返回 Socket 通道的连接状态,即连接与否。

  6. open() and open((SocketAddress remote) - open 方法用于打开一个没有指定地址的 Socket 通道,而带参数的 open 方法用于打开一个具有指定远程地址的通道,并连接到它。该方便方法的工作方式如下:调用 open() 方法,调用结果 Socket 通道的 connect 方法,将 remote 传递给它,然后返回该通道。

  7. read(ByteBuffer dst) - 该方法用于通过 Socket 通道从给定的缓冲区中读取数据。

  8. isConnectionPending() - 该方法指示此通道上是否正在进行连接操作。

Example

以下示例说明如何从 Java NIO SocketChannel 发送数据。

C:/Test/temp.txt

Hello World!

Client: SocketChannelClient.java

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.EnumSet;

public class SocketChannelClient {
   public static void main(String[] args) throws IOException {
      ServerSocketChannel serverSocket = null;
      SocketChannel client = null;
      serverSocket = ServerSocketChannel.open();
      serverSocket.socket().bind(new InetSocketAddress(9000));
      client = serverSocket.accept();
      System.out.println("Connection Set:  " + client.getRemoteAddress());
      Path path = Paths.get("C:/Test/temp1.txt");
      FileChannel fileChannel = FileChannel.open(path,
         EnumSet.of(StandardOpenOption.CREATE,
            StandardOpenOption.TRUNCATE_EXISTING,
            StandardOpenOption.WRITE)
         );
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      while(client.read(buffer) > 0) {
         buffer.flip();
         fileChannel.write(buffer);
         buffer.clear();
      }
      fileChannel.close();
      System.out.println("File Received");
      client.close();
   }
}

Output

在服务器启动前运行客户端不会打印任何内容。

Server: SocketChannelServer.java

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Path;
import java.nio.file.Paths;

public class SocketChannelServer {
   public static void main(String[] args) throws IOException {
      SocketChannel server = SocketChannel.open();
      SocketAddress socketAddr = new InetSocketAddress("localhost", 9000);
      server.connect(socketAddr);

      Path path = Paths.get("C:/Test/temp.txt");
      FileChannel fileChannel = FileChannel.open(path);
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      while(fileChannel.read(buffer) > 0) {
         buffer.flip();
         server.write(buffer);
         buffer.clear();
      }
      fileChannel.close();
      System.out.println("File Sent");
      server.close();
   }
}

Output

运行服务器将打印以下内容。

Connection Set:  /127.0.0.1:49558
File Received