目錄
1.前言
2.正文
2.1ServerSocket類
2.2Socket類
2.3Tcp回顯服務器
2.3.1TcpEchoServer
2.3.2TcpEchoClient
3.小結
1.前言
哈嘍大家好,今天繼續進行計算機網絡的初階學習,今天學習的是tcp回顯服務器的實現,正文開始
2.正文
在正式講解Tcp回顯服務器,還要介紹兩個包,一個是ServerSocket包,這個包是專門給服務器用的,Socket包是服務器和客戶端都會用,下文詳解。
2.1ServerSocket類
Java中的ServerSocket
類是用于在服務器端監聽客戶端連接請求的核心類,屬于java.net
包。它允許服務器應用程序在指定端口上等待客戶端的連接,并為每個連接創建一個Socket
對象進行通信。
核心作用
監聽端口:綁定到特定端口,等待客戶端連接。
接受連接:通過
accept()
方法阻塞等待客戶端連接,返回代表客戶端的Socket
對象。管理連接隊列:通過
backlog
參數設置等待連接隊列的最大長度。關鍵方法
構造方法
ServerSocket(int port)
:綁定到指定端口。
ServerSocket(int port, int backlog)
:指定端口和連接隊列長度。
ServerSocket(int port, int backlog, InetAddress bindAddr)
:綁定到特定IP地址的端口。核心方法
Socket accept()
:阻塞等待客戶端連接,返回連接的Socket
對象。
void bind(SocketAddress endpoint)
:綁定到指定地址和端口。
void close()
:關閉服務器套接字。
int getLocalPort()
:獲取監聽的端口號。
void setSoTimeout(int timeout)
:設置accept()
的超時時間(毫秒)。使用流程
創建
ServerSocket
并綁定端口。循環調用
accept()
接受客戶端連接。為每個連接的
Socket
啟動新線程處理請求。處理完成后關閉資源。
2.2Socket類
Java中的Socket
類是用于實現網絡通信的核心類,屬于java.net
包。它代表客戶端與服務器之間的一個連接,允許通過輸入流和輸出流進行雙向數據傳輸。Socket
類通常與ServerSocket
類配合使用,實現客戶端-服務器模型的通信。
核心作用
建立連接:連接到服務器端的指定IP地址和端口。
數據傳輸:通過輸入流(
InputStream
)和輸出流(OutputStream
)進行數據交換。關閉連接:釋放資源并終止通信。
關鍵方法
構造方法
Socket(String host, int port)
:連接到指定主機和端口。
Socket(InetAddress address, int port)
:使用InetAddress
對象連接到指定主機和端口。
Socket(String host, int port, InetAddress localAddr, int localPort)
:綁定到本地地址和端口,同時連接到遠程主機。核心方法
InputStream getInputStream()
:獲取輸入流,用于讀取服務器發送的數據。
OutputStream getOutputStream()
:獲取輸出流,用于向服務器發送數據。
void close()
:關閉套接字,釋放資源。
void shutdownInput()
:關閉輸入流。
void shutdownOutput()
:關閉輸出流。
boolean isConnected()
:檢查是否已連接。
boolean isClosed()
:檢查是否已關閉。使用流程
創建
Socket
對象并連接到服務器。獲取輸入流和輸出流進行數據交換。
處理數據并完成通信。
關閉
Socket
和相關資源。
2.3Tcp回顯服務器
2.3.1TcpEchoServer
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
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;/*** TCP回顯服務器實現類* 功能:接收客戶端消息并原樣返回(回顯)*/
public class TcpEchoServer {private ServerSocket serverSocket = null; // 服務器套接字對象// 構造方法:初始化服務器并綁定端口public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port); // 創建ServerSocket并綁定指定端口}/*** 啟動服務器核心邏輯*/public void start() throws IOException {System.out.println("啟動服務器");// 創建線程池(動態調整線程數量,適合短任務)ExecutorService executorService = Executors.newCachedThreadPool();// 持續監聽客戶端連接while (true) {// 阻塞等待客戶端連接Socket clientSocket = serverSocket.accept();// 將客戶端連接提交給線程池處理executorService.submit(() -> {try {processConnection(clientSocket); // 處理單個客戶端連接} catch (IOException e) {e.printStackTrace();}});}}/*** 處理單個客戶端連接的完整生命周期* @param clientSocket 客戶端套接字對象*/private void processConnection(Socket clientSocket) throws IOException {// 打印客戶端連接信息System.out.printf("[%s:%d] 客戶端上線!\n", clientSocket.getInetAddress(), clientSocket.getPort());// 使用try-with-resources自動關閉流try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {// 使用Scanner和PrintWriter包裝流對象Scanner scanner = new Scanner(inputStream); // 輸入流掃描器PrintWriter printWriter = new PrintWriter(outputStream); // 輸出流寫入器// 持續處理客戶端請求while (true) {// 1. 檢測連接狀態(若輸入流中沒有數據,說明客戶端斷開)if (!scanner.hasNext()) {System.out.printf("[%s:%d] 客戶端下線!\n", clientSocket.getInetAddress(), clientSocket.getPort());break;}// 2. 讀取請求并處理String request = scanner.next(); // 讀取客戶端請求(空格分隔)String response = process(request); // 處理請求生成響應// 3. 返回響應給客戶端printWriter.println(response); // 寫入響應printWriter.flush(); // 強制刷新緩沖區}} catch (IOException e) {throw new RuntimeException(e);} finally {try {clientSocket.close(); // 確保關閉客戶端套接字} catch (IOException e) {throw new RuntimeException(e);}}}/*** 處理請求的核心邏輯(示例為簡單回顯)* @param request 客戶端請求內容* @return 返回與請求相同的字符串*/private String process(String request) {// 此處可添加業務邏輯(示例直接返回原內容)return request;}/*** 主方法:啟動服務器實例*/public static void main(String[] args) throws IOException {TcpEchoServer tcpEchoServer = new TcpEchoServer(9090); // 創建服務器實例(端口9090)tcpEchoServer.start(); // 啟動服務器}
}
核心思路講解:
整體架構設計:
該代碼實現了一個多線程TCP回顯服務器,核心功能是接收客戶端發送的文本消息,并將消息原樣返回(回顯)。其架構設計遵循經典客戶端-服務器模型,核心特點包括:
多線程處理:通過線程池動態分配線程,避免單線程阻塞導致的性能瓶頸。
資源自動管理:利用
try-with-resources
確保流和套接字的自動釋放。松耦合設計:將連接處理(
processConnection
)與業務邏輯(process
)分離,便于擴展。
核心組件解析:
(1) ServerSocket 的職責
端口監聽:綁定到指定端口(如9090),通過
accept()
阻塞等待客戶端連接。連接隊列管理:默認使用操作系統提供的連接隊列(通過
backlog
參數可調整隊列長度)。生命周期控制:服務器運行時持續監聽,直到進程終止(代碼中未實現優雅關閉邏輯)。
(2) Socket 連接處理
雙向通信:每個客戶端連接對應一個
Socket
對象,通過其輸入流(InputStream
)和輸出流(OutputStream
)實現數據交換。連接狀態檢測:通過
scanner.hasNext()
判斷客戶端是否斷開(輸入流關閉時返回false
)。(3) 線程池的作用
動態資源分配:
Executors.newCachedThreadPool()
創建的線程池會根據任務量自動擴展/收縮:
空閑線程默認存活60秒后被回收。
適合短生命周期任務(如HTTP請求)。
避免線程爆炸:相比為每個連接直接創建
new Thread()
,線程池能有效控制系統資源占用。
3. 關鍵流程詳解:
(1) 啟動階段
初始化
ServerSocket
并綁定端口。創建線程池,進入無限循環等待連接。
(2) 連接處理階段
接受連接:
accept()
返回客戶端Socket
對象。提交任務:將
processConnection
方法包裝為任務提交到線程池。處理請求:
通過
Scanner
逐詞讀取客戶端請求(空格分隔)。調用
process()
生成響應(此處簡單回顯)。通過
PrintWriter
寫回響應并強制刷新緩沖區。(3) 連接終止
客戶端主動斷開:
scanner.hasNext()
檢測到輸入流結束,跳出循環關閉連接。異常處理:捕獲
IOException
并關閉套接字,防止資源泄漏。
2.3.2TcpEchoClient
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;public class TcpEchoClient {// 定義一個Socket對象,用于與服務器建立連接private Socket socket = null;// 構造函數,用于初始化客戶端并連接到指定的服務器IP和端口private TcpEchoClient(String serverIp, int serverPort) throws IOException {// 創建一個Socket對象,連接到指定的服務器IP和端口socket = new Socket(serverIp, serverPort);}// 啟動客戶端,開始與服務器進行通信public void start() {// 創建一個Scanner對象,用于從控制臺讀取用戶輸入Scanner scanner = new Scanner(System.in);try (// 獲取Socket的輸入流,用于接收服務器發送的數據InputStream inputStream = socket.getInputStream();// 獲取Socket的輸出流,用于向服務器發送數據OutputStream outputStream = socket.getOutputStream()) {// 創建一個Scanner對象,用于從輸入流中讀取服務器發送的數據Scanner scannerNet = new Scanner(inputStream);// 創建一個PrintWriter對象,用于向輸出流中寫入數據PrintWriter writer = new PrintWriter(outputStream);// 進入一個無限循環,持續與服務器進行通信while (true) {// 從控制臺讀取用戶輸入的數據String request = scanner.next();// 將用戶輸入的數據發送到服務器writer.println(request);// 刷新輸出流,確保數據被發送writer.flush();// 從服務器讀取響應數據String response = scannerNet.next();// 將服務器返回的響應數據打印到控制臺System.out.println(response);}} catch (IOException e) {// 如果發生IO異常,拋出運行時異常throw new RuntimeException(e);}}// 主函數,程序的入口public static void main(String[] args) throws IOException {// 創建一個TcpEchoClient對象,連接到本地服務器的9090端口TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);// 啟動客戶端,開始與服務器通信client.start();}
}
?核心思路講解:
Socket連接:
在
TcpEchoClient
的構造函數中,通過new Socket(serverIp, serverPort)
創建一個Socket對象,連接到指定的服務器IP和端口。這個Socket對象將用于后續的通信。輸入輸出流:
在
start
方法中,通過socket.getInputStream()
和socket.getOutputStream()
分別獲取Socket的輸入流和輸出流。輸入流用于接收服務器發送的數據,輸出流用于向服務器發送數據。用戶輸入與服務器通信:
使用
Scanner
從控制臺讀取用戶輸入的數據,并通過PrintWriter
將數據發送到服務器。使用
Scanner
從輸入流中讀取服務器返回的響應數據,并將其打印到控制臺。循環通信:
通過一個無限循環
while (true)
,客戶端可以持續與服務器進行通信。每次循環中,客戶端都會讀取用戶輸入,發送到服務器,并等待服務器的響應。異常處理:
如果在通信過程中發生IO異常,代碼會捕獲該異常并拋出運行時異常。
主函數:
在
main
函數中,創建一個TcpEchoClient
對象,并連接到本地服務器的9090端口。然后調用start
方法,啟動客戶端與服務器的通信。
3.小結
今天的分享到這里就結束了,喜歡的小伙伴不要忘記點點贊點個關注,你的鼓勵就是對我最大的支持,加油!