實現伺服器和客戶端資料互動,Java Socket有妙招

wuyibnsk發表於2021-10-26


 

本文分享自華為雲社群《Java Socket 如何實現伺服器和客戶端資料互動》,作者 : jackwangcumt 。

GoodMai 好買網

   IT技術交易平臺

1 Socket概述

根據百度百科的定義,Socket 譯為套接字,它是對網路中不同主機上的應用程式之間進行雙向通訊的端點的抽象。一個Socket例項就是網路上程式通訊的一端,提供了應用層程式利用網路協議交換資料的機制。Socket向上連線各種應用程式,向下連線各種網路協議,是應用程式透過網路協議進行通訊的介面。其示意圖如下下圖所示:

 

 

(來自《Java TCP/IP Socket程式設計》)

 

從上圖可知,套接字Socket在OSI七層模型中處於應用層和傳輸層之間,是一個重要的介面。一般來說,傳輸層協議有TCP協議和UDP協議。這兩個協議底層都基於IP網路層協議。

 

2 Java Socket 實現

Java SDK中,對於Socket原生提供了支援,它分為ServerSocket和Socket,其中ServerSocket發起一個服務端的Socket,其中需要提供一個埠號,如果給定0,則自動申請可用的埠。當然了,也可以指定具體的埠號(有一定的範圍,不超過 65535 )。不過這裡需要注意,傳入的埠不能被其他應用佔用,否則啟動服務失敗。下面給出一個簡單的ServerSocket示例,程式碼如下:

 

package com.example.demo.network;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyServer {
    //<= 65535
    private static final int PORT = 65535;
 
    public static void main(String[] args) throws Exception {
 
        try (ServerSocket listener = new ServerSocket(PORT)) {
            System.out.println("Server Started");
            //執行緒池大小,它根據客戶端數來決定,當大於10時,則之前的10個執行緒仍然可以工作,
            // 而超過的執行緒則進入佇列中,等待。
            //當之前的客戶端釋放量後,則在佇列中的執行緒仍然可以工作。
            ExecutorService pool = Executors.newFixedThreadPool(10);
            while (true) {
                //多執行緒
                pool.execute(new MyServerDemo01(listener.accept()));
                System.out.println(pool);
            }
        }
    }
    //Runnable介面的實現物件可以被執行緒Thread呼叫
    private static class MyServerDemo01 implements Runnable {
 
        private Socket socket;
 
        MyServerDemo01(Socket socket) {
            this.socket = socket;
        }
 
      
        public void run() {
            System.out.println("Client [" + socket.getRemoteSocketAddress().toString()+" ] Connected");
            try {
                //輸入
                Scanner in = new Scanner(socket.getInputStream());
                //輸出
                PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
                while (in.hasNextLine()) {
                    String msg = in.nextLine();
                    System.out.println("Client [" + socket.getRemoteSocketAddress().toString()+" ] : " + msg);
                    out.println(msg.toUpperCase());
                }
            } catch (Exception e) {
                System.out.println("Error:" + socket+ e.getMessage());
            } finally {
                try {
                    //關閉socket
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                System.out.println("Closed: " + socket);
            }
        }
    }
}

 

啟動該Server端,並開始監聽客戶端的連線。當客戶端沒有連線時,伺服器執行緒池pool並未啟動單獨的執行緒。下面給出客戶端的Java Socket實現,具體的示例程式碼如下:



package com.example.demo.network;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class MyClient {
    //<= 65535
    private static final int PORT = 65535;
    //伺服器地址
    private static final String IP = "127.0.0.1";
 
    public static void main(String[] args) throws Exception {
 
        try (Socket socket = new Socket(IP, PORT)) {
            System.out.println("Client ["+socket.getRemoteSocketAddress().toString()+" ] Started");
            Scanner scanner = new Scanner(System.in);
            Scanner in = new Scanner(socket.getInputStream());
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
            while (scanner.hasNextLine()) {
                out.println(scanner.nextLine());
                System.out.println("Server Response:"+ in.nextLine());
            }
        }catch (Exception ex){
            System.out.println("Error : "+ ex.getMessage());
        }
    }
}

 

從程式碼可知,try (Socket socket = new Socket(IP, PORT)) 是一種包含資源釋放的try-with-resource機制,它會自動進行資源釋放,而不需要手動進行釋放。Socket物件要想和伺服器通訊,必須要明確伺服器的IP地址和埠,否則不能正確通訊,Socket啟動時,也會在主機上佔用自己的埠。我們首先啟動Server端,然後可以同時啟動10個以上的Client端,比如13個,那麼超過Executors.newFixedThreadPool(10)限定的數量10後,將進入queued tasks佇列中進行排隊等待。透過列印pool物件,可以看出當前的狀態,比如[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 0]說明當前在執行狀態,執行緒池大小為10,啟用的執行緒為10,等待的任務執行緒queued tasks為0。

 

 

 

下面給出Server端相關輸出示例:

 

Server Started
java.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
Client [/127.0.0.1:64590 ] Connected
java.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]
Client [/127.0.0.1:64597 ] Connected
java.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 3, active threads = 3, queued tasks = 0, completed tasks = 0]
Client [/127.0.0.1:64603 ] Connected
Client [/127.0.0.1:64590 ] : hello
Client [/127.0.0.1:64590 ] : hello
Client [/127.0.0.1:64597 ] : world
Client [/127.0.0.1:64597 ] : world
Client [/127.0.0.1:64603 ] : python
Client [/127.0.0.1:64597 ] : python02
java.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 0]
Client [/127.0.0.1:57806 ] Connected
java.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 5, active threads = 5, queued tasks = 0, completed tasks = 0]
Client [/127.0.0.1:57814 ] Connected
java.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 6, active threads = 6, queued tasks = 0, completed tasks = 0]
Client [/127.0.0.1:57820 ] Connected
java.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 7, active threads = 7, queued tasks = 0, completed tasks = 0]
Client [/127.0.0.1:57827 ] Connected
java.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 8, active threads = 8, queued tasks = 0, completed tasks = 0]
Client [/127.0.0.1:57833 ] Connected
java.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 9, active threads = 9, queued tasks = 0, completed tasks = 0]
Client [/127.0.0.1:57839 ] Connected
java.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 10, active threads = 10, queued tasks = 0, completed tasks = 0]
Client [/127.0.0.1:57845 ] Connected
java.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 10, active threads = 10, queued tasks = 1, completed tasks = 0]
java.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 10, active threads = 10, queued tasks = 2, completed tasks = 0]
java.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 10, active threads = 10, queued tasks = 3, completed tasks = 0]
Closed: Socket[addr=/127.0.0.1,port=64590,localport=65535]
Client [/127.0.0.1:57854 ] Connected
Client [/127.0.0.1:57814 ] : t2
Client [/127.0.0.1:57814 ] : tw2
```


 

 

客戶端相關輸入輸出介面如下:

 


Client [/127.0.0.1:65535 ] Started
world
Server Response:WORLD
world
Server Response:WORLD
python02
Server Response:PYTHON02



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70008135/viewspace-2839263/,如需轉載,請註明出處,否則將追究法律責任。

相關文章