Java?Socket編程從零到實戰(zhàn)詳解(完整實戰(zhàn)案例)
一、Socket基礎(chǔ)概念與工作流程(圖解)
(先理解“打電話”模型,再寫代碼)
1. Socket通信核心模型
關(guān)鍵角色:
- 客戶端:主動發(fā)起連接(類似撥打電話)
- 服務端:監(jiān)聽端口,等待連接(類似待機電話)
- Socket對象:連接建立后的數(shù)據(jù)傳輸通道(通話線路)
2. 核心流程分解
- 服務端:創(chuàng)建
ServerSocket
→ 綁定端口 → 阻塞等待連接(accept()
) - 客戶端:創(chuàng)建
Socket
→ 指定服務端IP和端口 → 發(fā)起連接 - 雙向通信:通過輸入流(
InputStream
)和輸出流(OutputStream
)收發(fā)數(shù)據(jù) - 關(guān)閉連接:調(diào)用
close()
釋放資源
二、服務端與客戶端基礎(chǔ)代碼分步解析
(每行代碼加注釋,新手必看)
1. 服務端基礎(chǔ)代碼(單線程版)
// 步驟1:創(chuàng)建ServerSocket,綁定端口8080 ServerSocket serverSocket = new ServerSocket(8080); System.out.println("服務端啟動,等待連接..."); // 步驟2:等待客戶端連接(阻塞方法,直到有客戶端連接) Socket clientSocket = serverSocket.accept(); System.out.println("客戶端接入:" + clientSocket.getRemoteSocketAddress()); // 步驟3:獲取輸入流(接收客戶端數(shù)據(jù)) InputStream input = clientSocket.getInputStream(); byte[] buffer = new byte[1024]; int len = input.read(buffer); // 讀取數(shù)據(jù)到buffer數(shù)組 String receivedData = new String(buffer, 0, len); System.out.println("收到消息:" + receivedData); // 步驟4:發(fā)送響應數(shù)據(jù) OutputStream output = clientSocket.getOutputStream(); output.write("已收到!".getBytes()); // 步驟5:關(guān)閉連接(實際開發(fā)中需在finally塊處理) clientSocket.close(); serverSocket.close();
2. 客戶端基礎(chǔ)代碼
// 步驟1:連接服務端(IP+端口) Socket socket = new Socket("127.0.0.1", 8080); System.out.println("連接服務端成功!"); // 步驟2:發(fā)送數(shù)據(jù) OutputStream output = socket.getOutputStream(); output.write("你好,服務端!".getBytes()); // 步驟3:接收響應 InputStream input = socket.getInputStream(); byte[] buffer = new byte[1024]; int len = input.read(buffer); String response = new String(buffer, 0, len); System.out.println("服務端響應:" + response); // 步驟4:關(guān)閉連接 socket.close();
三、超時設(shè)置詳解(解決卡死問題)
(必學技能,避免程序無限等待)
1. 連接超時(防止無法連接時卡死)
Socket socket = new Socket(); // 設(shè)置連接超時為5秒(單位:毫秒) socket.connect(new InetSocketAddress("127.0.0.1", 8080), 5000); //
- 觸發(fā)場景:服務端未啟動或網(wǎng)絡不通
- 異常處理:捕獲
SocketTimeoutException
提示用戶檢查網(wǎng)絡
2. 讀取超時(防止數(shù)據(jù)未到達時阻塞)
socket.setSoTimeout(3000); // 設(shè)置讀取超時3秒
- 作用范圍:
InputStream.read()
操作 - 異常處理:超時后拋出
SocketTimeoutException
,可重試或終止
3. 完整超時處理示例
try (Socket socket = new Socket()) { // 連接超時5秒 socket.connect(new InetSocketAddress("127.0.0.1", 8080), 5000); // 讀取超時3秒 socket.setSoTimeout(3000); InputStream input = socket.getInputStream(); // 讀取數(shù)據(jù)... } catch (SocketTimeoutException e) { System.err.println("操作超時:" + e.getMessage()); } catch (IOException e) { System.err.println("連接失?。? + e.getMessage()); }
四、心跳機制實現(xiàn)(維持長連接)
(防止長時間無數(shù)據(jù)導致連接斷開)
1. 心跳包原理
- 作用:定時發(fā)送空數(shù)據(jù)包,告知對方連接存活
- 實現(xiàn)方式:客戶端定時任務 + 服務端超時檢測
2. 客戶端心跳代碼
Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { try { OutputStream out = socket.getOutputStream(); out.write(0); // 發(fā)送心跳包(內(nèi)容可為任意約定標識) out.flush(); System.out.println("心跳發(fā)送成功"); } catch (IOException e) { System.err.println("心跳發(fā)送失敗,連接已斷開"); timer.cancel(); // 停止定時任務 } } }, 0, 30000); // 立即啟動,每30秒執(zhí)行一次
3. 服務端檢測心跳
socket.setSoTimeout(45000); // 超時時間略大于心跳間隔 try { InputStream in = socket.getInputStream(); while (true) { int data = in.read(); // 阻塞等待數(shù)據(jù) if (data == 0) { System.out.println("收到心跳包"); } } } catch (SocketTimeoutException e) { System.err.println("心跳超時,連接斷開"); socket.close(); }
五、完整實戰(zhàn)案例:帶超時與心跳的Echo服務
服務端代碼(多線程版)
public class EchoServer { public static void main(String[] args) throws IOException { ExecutorService pool = Executors.newCachedThreadPool(); // 線程池處理并發(fā) try (ServerSocket server = new ServerSocket(8080)) { System.out.println("服務端啟動,端口8080"); while (true) { Socket client = server.accept(); client.setSoTimeout(45000); // 設(shè)置讀取超時45秒 pool.submit(() -> handleClient(client)); } } } private static void handleClient(Socket client) { try (BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream())); PrintWriter out = new PrintWriter(client.getOutputStream(), true)) { String input; while ((input = in.readLine()) != null) { if ("HEARTBEAT".equals(input)) { // 識別心跳包 System.out.println("收到心跳包"); continue; } out.println("Echo: " + input); // 回顯消息 } } catch (SocketTimeoutException e) { System.err.println("客戶端超時未響應,連接關(guān)閉"); } catch (IOException e) { e.printStackTrace(); } finally { try { client.close(); } catch (IOException e) {} } } }
客戶端代碼(帶心跳與超時)
public class EchoClient { public static void main(String[] args) { try (Socket socket = new Socket()) { // 連接超時5秒 socket.connect(new InetSocketAddress("127.0.0.1", 8080), 5000); // 讀取超時3秒 socket.setSoTimeout(3000); // 啟動心跳線程(每30秒一次) startHeartbeat(socket.getOutputStream()); Scanner scanner = new Scanner(System.in); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); while (true) { System.out.print("輸入消息:"); String msg = scanner.nextLine(); out.println(msg); // 發(fā)送消息 System.out.println("服務端響應:" + in.readLine()); } } catch (SocketTimeoutException e) { System.err.println("操作超時:" + e.getMessage()); } catch (IOException e) { System.err.println("連接異常:" + e.getMessage()); } } private static void startHeartbeat(OutputStream out) { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { try { out.write("HEARTBEAT\n".getBytes()); // 發(fā)送心跳標識 out.flush(); } catch (IOException e) { timer.cancel(); } } }, 0, 30000); } }
六、常見問題與解決方案速查表
問題現(xiàn)象 | 可能原因 | 解決方案 |
---|---|---|
Connection refused | 服務端未啟動或端口錯誤 | 檢查服務端代碼是否運行,確認端口一致 |
Read timed out | 網(wǎng)絡延遲或服務端未及時響應 | 增加超時時間或優(yōu)化服務端代碼 |
Broken pipe | 連接已關(guān)閉仍嘗試寫數(shù)據(jù) | 發(fā)送前檢查socket.isClosed() ,捕獲異常后重連 |
內(nèi)存泄漏 | 未關(guān)閉Socket或流 | 使用try-with-resources 自動關(guān)閉資源 |
七、Java Socket核心方法速查表
方法名 | 所屬類 | 功能描述 | 參數(shù)說明 | 返回值 | 常見異常 | 使用示例 |
---|---|---|---|---|---|---|
ServerSocket(int port) | ServerSocket | 創(chuàng)建服務端Socket并綁定指定端口 | port :監(jiān)聽的端口號(0-65535) | 無 | BindException (端口被占用) | new ServerSocket(8080); |
accept() | ServerSocket | 阻塞等待客戶端連接,返回通信用的Socket對象 | 無 | Socket (客戶端連接對象) | IOException | Socket client = serverSocket.accept(); |
close() | ServerSocket | 關(guān)閉服務端Socket,釋放端口資源 | 無 | 無 | IOException | serverSocket.close(); |
Socket(String host, int port) | Socket | 客戶端主動連接服務端(構(gòu)造函數(shù)隱式調(diào)用connect() ) | host :服務端IP;port :服務端端口 | 無 | UnknownHostException , IOException | Socket socket = new Socket("127.0.0.1", 8080); |
connect(SocketAddress addr, int timeout) | Socket | 顯式連接服務端,可設(shè)置超時時間 | addr :服務端地址;timeout :超時毫秒 | 無 | SocketTimeoutException | socket.connect(new InetSocketAddress("127.0.0.1", 8080), 5000); |
getInputStream() | Socket | 獲取輸入流,用于接收數(shù)據(jù) | 無 | InputStream | IOException | InputStream in = socket.getInputStream(); |
getOutputStream() | Socket | 獲取輸出流,用于發(fā)送數(shù)據(jù) | 無 | OutputStream | IOException | OutputStream out = socket.getOutputStream(); |
setSoTimeout(int timeout) | Socket | 設(shè)置讀取超時時間(單位:毫秒),超時后拋出SocketTimeoutException | timeout :超時時間(0表示無限等待) | 無 | SocketException | socket.setSoTimeout(3000); |
setKeepAlive(boolean on) | Socket | 啟用/禁用TCP?;顧C制(默認關(guān)閉),自動檢測連接是否存活 | on :true啟用,false禁用 | 無 | SocketException | socket.setKeepAlive(true); |
shutdownOutput() | Socket | 關(guān)閉輸出流(發(fā)送FIN包),通知對方數(shù)據(jù)發(fā)送完畢,但不關(guān)閉Socket | 無 | 無 | IOException | socket.shutdownOutput(); |
close() | Socket | 關(guān)閉Socket連接,釋放資源 | 無 | 無 | IOException | socket.close(); |
readInt() | DataInputStream | 從輸入流讀取4字節(jié)的int值(常用于解析長度頭) | 無 | int | EOFException , IOException | int length = new DataInputStream(in).readInt(); |
writeInt(int v) | DataOutputStream | 向輸出流寫入4字節(jié)的int值(常用于發(fā)送長度頭) | v :要寫入的整數(shù)值 | 無 | IOException | new DataOutputStream(out).writeInt(1024); |
到此這篇關(guān)于Java Socket編程從零到實戰(zhàn)詳解的文章就介紹到這了,更多相關(guān)Java Socket編程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決maven中只有Lifecycle而Dependencies和Plugins消失的問題
這篇文章主要介紹了maven中只有Lifecycle而Dependencies和Plugins消失的問題及解決方法,本文通過圖文的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2020-07-07

Java數(shù)據(jù)結(jié)構(gòu)學習之樹

intellij idea 2021.2 打包并上傳運行spring boot項目的詳細過程(spring boot 2

淺談Arrays.asList() 和ArrayList類型區(qū)別