前幾天介紹了計算機網路的一些概念,並介紹了幾個協議。下面就說說 Java 中的 Socket 編程,伺服器和客戶端是如何通信的呢?

首先要介紹一下 Socket ,我們知道在 TCP/IP 協議簇中,TCP、UDP 協議都是在傳輸層,應用層基於傳輸層進行通信。而 Socket 可以看成是對 TCP 、UDP 協議的實現。具體到編程的時候,要看業務選擇是使用 TCP 還是 UDP 協議。今天主要講的就是基於 TCP 通信的 Socket 實現。若你對 TCP 還不熟悉。可以看這篇文章。

Java 中為 TCP 協議提供了兩個類:Socket 類和 ServerSocket 類。一個 Socket 實例代表了 TCP 連接的一個客戶端,而一個 ServerSocket 實例代表了 TCP 連接的一個伺服器端,一般在 TCP Socket 編程中,客戶端有多個,而伺服器端只有一個,客戶端 TCP 向伺服器端 TCP 發送連接請求,伺服器端的 ServerSocket 實例則監聽來自客戶端的 TCP 連接請求,並為每個請求創建新的 Socket 實例,由於服務端在調用 accept()等待客戶端的連接請求時會阻塞,直到收到客戶端發送的連接請求才會繼續往下執行代碼,因此要為每個 Socket 連接開啟一個線程(這裡就是多線程的應用啊)。伺服器端要同時處理 ServerSocket 實例和 Socket 實例,而客戶端只需要使用 Socket 實例。

另外,每個 Socket 實例會關聯一個 InputStream 和 OutputStream 對象,我們通過將位元組寫入 Socket 的 OutputStream 來發送數據,並通過從 InputStream 來接收數據。

好吧,上面的描述可能有點懵,下面就來看一個 demo。使用 Socket 實現一個簡單的交互,在伺服器端使用多線程來處理請求。

客戶端實現如下:

public class Client {
public static void main(String[] args) throws IOException {
Socket socket = null;
PrintWriter pw = null;
BufferedReader br = null;
try {
// 創建Socket對象,指明需要連接的伺服器地址和埠
socket = new Socket("localhost", 6688);

// 連接建立後,通過 Socket 輸出流向伺服器端發送請求信息
pw = new PrintWriter(socket.getOutputStream());
pw.write("Hello , server . Im Client !");
pw.flush();
socket.shutdownOutput();

// 通過輸入流獲取伺服器端返回的響應信息;
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String info = null;
while((info = br.readLine()) != null){
System.out.println("伺服器返回信息: "+ info);
}
socket.shutdownInput();

----後面的錯誤處理和關閉資源省略-----
}
}

伺服器端實現如下:

public class Server {
public static void main(String[] args) throws IOException {
Socket socket = null;
try {
// 創建ServerSocket對象,綁定監聽埠
ServerSocket serverSocket = new ServerSocket(6688);
while(true){
// 通過accept()方法監聽客戶端請求
socket =serverSocket.accept();
ServerThread serverThread = new ServerThread(socket);
serverThread.start();
}
}
}

線程具體實現如下:

public class ServerThread extends Thread {
Socket socket = null;
BufferedReader br = null;
PrintWriter pw = null;

public ServerThread(Socket socket){
this.socket = socket;
}

@Override
public void run() {
try {
// 連接建立後,通過輸入流讀取客戶端發送的請求信息 msg
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
StringBuffer msg = new StringBuffer();
String info = null;
while((info = br.readLine()) != null){
msg.append(info);
}
System.out.println("伺服器收到 [ "+ socket.getInetAddress()+" ] 的消息 [ " + msg+" ]");
socket.shutdownInput();

// 通過輸出流向客戶端發送相應信息
pw = new PrintWriter(socket.getOutputStream());
pw.write(" success !");
pw.flush();
socket.shutdownOutput();
}
}
}

總結一下 Socket TCP 中實戰的步驟。

伺服器端:

(1) 創建ServerSocket對象,綁定監聽埠;

(2) 通過accept()方法監聽客戶端請求;

(3) 連接建立後,通過輸入流讀取客戶端發送的請求信息;

(4) 通過輸出流向客戶端發送相應信息;

(5) 關閉響應資源。

客戶端:

(1) 創建Socket對象,指明需要連接的伺服器地址和埠;

(2) 連接建立後,通過輸出流向伺服器端發送請求信息;

(3) 通過輸入流獲取伺服器端返回的響應信息;

(4) 關閉響應資源。

注意:

1 首先執行伺服器端代碼。

2 伺服器端執行之後默認就一直在等待客戶端的連接請求。

3 以上只是一個非常基礎的案例,這只是 Socket 編程的冰山一角。

4 可以優化的地方還有很多,伺服器端參數的優化,如,接受數據的緩衝區大小、等待客戶端連接的最長時間、使用線程池處理請求等。

PS. 歡迎關注我的個人公眾號:【yujikui1115】 我們一起奮鬥。


推薦閱讀:
查看原文 >>
相關文章