天天看點

Java Socket:飛鴿傳書的網絡套接字(2)

03、ServerSocket 執行個體

接下來,我們模拟一個遠端服務,通過 java.net.ServerSocket 實作。代碼示例如下。

try (ServerSocket server = new ServerSocket(8888);
        Socket socket = server.accept();
        InputStream is = socket.getInputStream();
        OutputStream os = socket.getOutputStream();
        Scanner scanner = new Scanner(is)) {
    PrintWriter pw = new PrintWriter(new OutputStreamWriter(os, "gbk"), true);
    pw.println("你好啊,歡迎關注「沉默王二」 公衆号,回複關鍵字「2048」 領取程式員進階必讀資料包");
    boolean done = false;
    while (!done && scanner.hasNextLine()) {
        String line = scanner.nextLine();
        System.out.println(line);
        if ("2048".equals(line)) {
            done = true;
        }
    }
} catch (UnknownHostException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}      

1)建立伺服器端的套接字也比較簡單,隻需要指定一個能夠獨占的端口号就可以了(0~1023 這些端口都已經被系統預留了)。

ServerSocket server = new ServerSocket(8888);

2)調用 ServerSocket 對象的 accept() 等待用戶端套接字的連接配接請求。一旦監聽到用戶端的套接字請求,就會傳回一個表示連接配接已建立的 Socket 對象,可以從中擷取到輸入流和輸出流。

Socket socket = server.accept();

InputStream is = socket.getInputStream();

OutputStream os = socket.getOutputStream();

用戶端套接字發送的所有資訊都會包裹在伺服器端套接字的輸入流中;而伺服器端套接字發送的所有資訊都會包裹在用戶端套接字的輸出流中。

3)伺服器端可以通過以下代碼向用戶端發送消息。

PrintWriter pw = new PrintWriter(new OutputStreamWriter(os, "gbk"), true);

pw.println("你好啊,歡迎關注「沉默王二」 公衆号,回複關鍵字「2048」 領取程式員進階必讀資料包");

4)伺服器端可以通過以下代碼讀取用戶端發送過來的消息。

Scanner scanner = new Scanner(is);
boolean done = false;
while (!done && scanner.hasNextLine()) {
    String line = scanner.nextLine();
    System.out.println(line);
    if ("2048".equals(line)) {
        done = true;
    }
}      

運作該服務後,可以通過 telnet localhost 8888 指令連接配接該遠端服務,不出所料,你将會看到以下資訊。

Java Socket:飛鴿傳書的網絡套接字(2)

PS:可以在目前指令視窗中輸入 2048,服務端收到該消息後會中斷該套接字連接配接(目前視窗會顯示“遺失對主機的連接配接”)。

04、為多個用戶端服務

非常遺憾的是,上面的例子中,伺服器端隻能為一個用戶端服務——這不符合伺服器端一對多的要求。

優化方案也非常簡單(你應該也能想得到):伺服器端接收到用戶端的套接字請求時,可以啟動一個線程來處理,而主程式繼續等待下一個連接配接。代碼示例如下。

try (ServerSocket server = new ServerSocket(8888)) {
    while (true) {
        Socket socket = server.accept();
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
              // 套接字處理程式
            }
        });
        thread.start();
    }
} catch (IOException e) {
    e.printStackTrace();
}      

線程内部(run(){} 方法裡)用來處理套接字,代碼示例如下:

try {
    InputStream is = socket.getInputStream();
    OutputStream os = socket.getOutputStream();
    Scanner scanner = new Scanner(is);
   // 其他代碼省略
   // 向用戶端發送消息
   // 讀取用戶端發送過來的消息
} catch (IOException e) {
    e.printStackTrace();
} finally {
    try {
        socket.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}      

伺服器端代碼優化後重新運作,你就可以通過 telnet 指令測試了。打開一個指令行視窗輸入 telnet localhost 8888,再打開一個新的指令行視窗輸入 telnet localhost 8888,多個視窗都可以和伺服器端進行通信,除非伺服器端代碼中斷運作。

05、最後

如今大多數基于網絡的軟體,如浏覽器、即時通訊工具甚至是 P2P 下載下傳都是基于 Socket 實作的,是以掌握 Java Socket 程式設計還是蠻有必要的。Socket 程式設計也比較有趣,很多初學者都會編寫一兩個基于“用戶端-伺服器端”的小程式來提高自己的程式設計水準,建議你也試一試。