Java NIO - ServerSocket 通道



Java NIO 伺服器套接字通道也是一種可選擇的通道型別,用於連線套接字的面向流的資料流。如果不存在任何預先存在的套接字,則可以透過呼叫其靜態open()方法建立伺服器套接字通道。伺服器套接字通道是透過呼叫 open 方法建立的,但尚未繫結。為了繫結套接字通道,必須呼叫bind()方法。

這裡需要提到的一點是,如果通道未繫結且嘗試執行任何 I/O 操作,則此通道將丟擲 NotYetBoundException。因此,必須確保在執行任何 I/O 操作之前繫結通道。

伺服器套接字通道的傳入連線透過呼叫 ServerSocketChannel.accept() 方法來監聽。當 accept() 方法返回時,它返回一個帶有傳入連線的 SocketChannel。因此,accept() 方法會阻塞,直到到達傳入連線。如果通道處於非阻塞模式,則如果沒有任何待處理的連線,accept 方法將立即返回 null。否則,它將無限期地阻塞,直到新的連線可用或發生 I/O 錯誤。

新通道的套接字最初未繫結;必須透過其套接字的 bind 方法之一將其繫結到特定地址,然後才能接受連線。此外,新通道是透過呼叫系統範圍的預設 SelectorProvider 物件的 openServerSocketChannel 方法建立的。

像套接字通道一樣,伺服器套接字通道可以使用read()方法讀取資料。首先分配緩衝區。從 ServerSocketChannel 讀取的資料儲存到緩衝區中。其次,我們呼叫 ServerSocketChannel.read() 方法,它將資料從 ServerSocketChannel 讀取到緩衝區中。read() 方法的整數值返回寫入緩衝區的位元組數。

同樣,可以使用write()方法將資料寫入伺服器套接字通道,並使用緩衝區作為引數。通常在 while 迴圈中使用 write 方法,因為需要重複 write() 方法,直到緩衝區沒有更多可寫的位元組。

套接字通道的重要方法

  • bind(SocketAddress local) − 此方法用於將套接字通道繫結到作為此方法引數提供的本地地址。

  • accept() − 此方法用於接受與此通道的套接字建立的連線。

  • connect(SocketAddress remote) − 此方法用於將套接字連線到遠端地址。

  • finishConnect() − 此方法用於完成連線套接字通道的過程。

  • getRemoteAddress() − 此方法返回通道的套接字連線到的遠端位置的地址。

  • isConnected() − 正如前面提到的,此方法返回套接字通道的連線狀態,即它是否已連線。

  • open() − open 方法用於為未指定的地址開啟套接字通道。此便捷方法的工作方式如同呼叫 open() 方法、對生成的伺服器套接字通道呼叫 connect 方法、將其傳遞給 remote,然後返回該通道。

  • read(ByteBuffer dst) − 此方法用於透過套接字通道從給定的緩衝區讀取資料。

  • setOption(SocketOption<T> name, T value) − 此方法設定套接字選項的值。

  • socket() − 此方法檢索與此通道關聯的伺服器套接字。

  • validOps() − 此方法返回一個操作集,該操作集標識此通道支援的操作。伺服器套接字通道僅支援接受新連線,因此此方法返回 SelectionKey.OP_ACCEPT。

示例

以下示例演示如何從 Java NIO ServerSocketChannel 傳送資料。

C:/Test/temp.txt

Hello World!

客戶端: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();
   }
}

輸出

執行客戶端在伺服器啟動之前不會列印任何內容。


伺服器: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();
   }
}

輸出

執行伺服器將列印以下內容。

Connection Set:  /127.0.0.1:49558
File Received
廣告
© . All rights reserved.