Java Nio 简明教程

Java NIO - ServerSocket Channel

Java NIO 服务器套接字通道是再次用于流式数据流连接套接字的可选择类型通道。服务器套接字通道可通过唤起其静态` open() 方法(在没有预先存在套接字的情况下提供)进行创建。通过唤起开放方法创建服务器套接字通道,但还未绑定。为了绑定套接字通道,需要调用 bind() `方法。

这里需要提到的一点是,如果通道没有绑定,并且尝试进行任何 I/O 操作,则此通道会抛出 NotYetBoundException。因此,在执行任何 IO 操作之前,必须确保通道已绑定。

通过调用 ServerSocketChannel.accept() 方法监听服务器套接字通道的传入连接。当 accept() 方法返回时,它会返回具有传入连接的 SocketChannel。因此,accept() 方法会一直阻止,直到传入连接到达为止。如果通道处于非阻塞模式,那么在没有挂起连接的情况下,accept 方法会立即返回 null。否则,它会无限期阻止,直到有可用的新连接或发生 I/O 错误。

新通道的套接字最初未绑定;必须通过其套接字的绑定方法之一将其绑定到特定地址,才能接受连接。新通道也可以通过唤起系统范围的默认 SelectorProvider 对象的 openServerSocketChannel 方法进行创建。

与套接字通道类似,服务器套接字通道可以使用` read() `方法读取数据。首先分配缓冲区。从 ServerSocketChannel 读入的数据存储在缓冲区中。其次,我们调用 ServerSocketChannel.read() 方法,它将数据从 ServerSocketChannel 读入缓冲区。read() 方法的整数值返回写入缓冲区的字节数。

同样地,可以使用` write() `方法将数据写入服务器套接字通道,并使用缓冲区作为参数。通常在 while 循环中使用 write 方法,因为需要重复 write() 方法,直到缓冲区中没有可写入的可用字节为止。

Important methods of Socket channel

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

  2. ` accept() ` - 此方法用于接受对该通道的套接字建立的连接。

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

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

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

  6. isConnected() - 如前所述,此方法返回套接字通道的连接状态,即是否已连接。

  7. ` open() ` - 打开方法用于为未指定地址打开套接字通道。此简易方法的作用类似于通过唤起 open() 方法,唤起所得服务器套接字通道的连接方法,为其传递远程,然后再返回该通道。

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

  9. ` setOption(SocketOption<T> name, T value) ` - 此方法设置套接字选项的值。

  10. ` socket() ` - 此方法检索与此通道关联的服务器套接字。

  11. ` validOps() ` - 此方法返回一个操作集,用于标识此通道支持的操作。服务器套接字通道仅支持接受新连接,因此此方法返回 SelectionKey.OP_ACCEPT。

Example

以下示例显示如何从 Java NIO ServerSocketChannel 发送数据。

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