Socket 編程之 TCP 實現
前幾天介紹了計算機網路的一些概念,並介紹了幾個協議。下面就說說 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】 我們一起奮鬥。
推薦閱讀: