一個簡單的 TCP 服務(wù)器

JDK 提供了 ServerSocket 類來代表 TCP 服務(wù)器的被動套接字。下面的代碼演示了一個簡單的 TCP 服務(wù)器(多線程阻塞模式),它不斷偵聽并接受客戶端的連接,然后將客戶端發(fā)送過來的文本按行讀取,全文轉(zhuǎn)換為大寫后返回給客戶端,直到客戶端發(fā)送文本行 bye:

  1. public class TcpServer implements Runnable {   
  2.     private ServerSocket serverSocket;   
  3.     
  4.     public TcpServer(int port) throws IOException {   
  5.         // 創(chuàng)建綁定到某個端口的 TCP 服務(wù)器被動套接字。   
  6.         serverSocket = new ServerSocket(port);   
  7.     }   
  8.     
  9.     @Override 
  10.     public void run() {   
  11.         while (true) {   
  12.             try {   
  13.                 // 以阻塞的方式接受一個客戶端連接,返回代表該連接的主動套接字。   
  14.                 Socket socket = serverSocket.accept();   
  15.                 // 在新線程中處理客戶端連接。   
  16.                 new Thread(new ClientHandler(socket)).start();   
  17.             } catch (IOException ex) {   
  18.                 ex.printStackTrace();   
  19.             }   
  20.         }   
  21.     }   
  22. }   
  23.     
  24. public class ClientHandler implements Runnable {   
  25.     private Socket socket;   
  26.     
  27.     public ClientHandler(Socket socket) {   
  28.         this.socket = Objects.requireNonNull(socket);   
  29.     }   
  30.     
  31.     @Override 
  32.     public void run() {   
  33.         try (Socket s = socket) {  // 減少代碼量的花招……   
  34.             // 包裝套接字的輸入流以讀取客戶端發(fā)送的文本行。   
  35.             BufferedReader in = new BufferedReader(new InputStreamReader(   
  36.                     s.getInputStream(), StandardCharsets.UTF_8));   
  37.             // 包裝套接字的輸出流以向客戶端發(fā)送轉(zhuǎn)換結(jié)果。   
  38.             PrintWriter out = new PrintWriter(new OutputStreamWriter(   
  39.                     s.getOutputStream(), StandardCharsets.UTF_8), true);   
  40.     
  41.             String line = null;   
  42.             while ((line = in.readLine()) != null) {   
  43.                 if (line.equals("bye")) {   
  44.                     break;   
  45.                 }   
  46.     
  47.                 // 將轉(zhuǎn)換結(jié)果輸出給客戶端。   
  48.                 out.println(line.toUpperCase(Locale.ENGLISH));   
  49.             }   
  50.         } catch (IOException ex) {   
  51.             ex.printStackTrace();   
  52.         }   
  53.     }   
  54. }  

阻塞模式的編程方式簡單,但存在性能問題,因為服務(wù)器線程會卡死在接受客戶端的 accept() 方法上,不能有效利用資源。套接字支持非阻塞模式,現(xiàn)在暫時略過。

一個簡單的 TCP 客戶端

JDK 提供了 Socket 類來代表 TCP 客戶端的主動套接字。下面的代碼演示了上述服務(wù)器的客戶端:

  1. public class TcpClient implements Runnable {   
  2.     private Socket socket;   
  3.     
  4.     public TcpClient(String host, int port) throws IOException {   
  5.         // 創(chuàng)建連接到服務(wù)器的套接字。   
  6.         socket = new Socket(host, port);   
  7.     }   
  8.     
  9.     @Override 
  10.     public void run() {   
  11.         try (Socket s = socket) {  // 再次減少代碼量……   
  12.             // 包裝套接字的輸出流以向服務(wù)器發(fā)送文本行。   
  13.             PrintWriter out = new PrintWriter(new OutputStreamWriter(   
  14.                     s.getOutputStream(), StandardCharsets.UTF_8), true);   
  15.             // 包裝套接字的輸入流以讀取服務(wù)器返回的文本行。   
  16.             BufferedReader in = new BufferedReader(new InputStreamReader(   
  17.                     s.getInputStream(), StandardCharsets.UTF_8));   
  18.     
  19.             Console console = System.console();   
  20.             String line = null;   
  21.             while ((line = console.readLine()) != null) {   
  22.                 if (line.equals("bye")) {   
  23.                     break;   
  24.                 }   
  25.     
  26.                 // 將文本行發(fā)送給服務(wù)器。   
  27.                 out.println(line);   
  28.                 // 打印服務(wù)器返回的文本行。   
  29.                 console.writer().println(in.readLine());   
  30.             }   
  31.     
  32.             // 通知服務(wù)器關(guān)閉連接。   
  33.             out.println("bye");   
  34.         } catch (IOException ex) {   
  35.             ex.printStackTrace();   
  36.         }   
  37.     }   
  38. }  

從 JDK 文檔可以看到,ServerSocket 和 Socket 在初始化的時候,可以設(shè)定一些參數(shù),還支持延遲綁定。這些東西對性能和行為都有所影響。后續(xù)兩篇文章將分別詳解這兩個類的初始化。

分享到

hanrui

相關(guān)推薦