首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 網路程式設計精通指南(13):Socket 基礎 — 連接、收發數據、關閉連接全流程

     編輯:Java 相關知識

Socket 是 Java 客戶端網路通訊的核心類。本文詳細講解建立連接、發送接收數據、關閉連接的標準三步流程,並深入剖析關閉機制的 4 種方式與流/連接狀態檢測方法。

標準三步流程

  1. 連接服務端 — new Socket(host, port) 或 connect()
  2. 發送/接收數據 — getOutputStream() / getInputStream()
  3. 關閉連接 — socket.close()

一、連接服務端

支援 IP 或域名直連,域名會自動經 DNS 解析:

// 命令行參數傳入目標主機
Socket socket = new Socket(args[0], 80); // 連接 80 端口
System.out.println(args[0] + " 已連接成功!");

異常處理:捕獲 UnknownHostException (域名無效) 與 IOException (連接失敗/超時)。

實用工具:端口掃描器 (多線程)

public class PortScanner extends Thread {
    private int minPort, maxPort;
    public PortScanner(int min, int max) { this.minPort = min; this.maxPort = max; }
    public void run() {
        for (int p = minPort; p <= maxPort; p++) {
            try (Socket s = new Socket("127.0.0.1", p)) {
                System.out.println(p + ": open");
            } catch (Exception ignored) {}
        }
    }
    public static void main(String[] args) {
        int min = Integer.parseInt(args[0]), max = Integer.parseInt(args[1]), threads = Integer.parseInt(args[2]);
        int range = (max - min + 1 + threads - 1) / threads;
        for (int i = 0; i < threads; i++) {
            new PortScanner(min + i * range, Math.min(max, min + (i+1)*range - 1)).start();
        }
    }
}

運行:java PortScanner 1 1000 20 — 20 線程掃描 1~1000 端口。

二、發送與接收數據

核心 API:getOutputStream() 發送,getInputStream() 接收。建議包裝為緩衝字符流:

Socket socket = new Socket("www.example.com", 80);

// 發送 HTTP 請求頭 (以 \r\n\r\n 結束)
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.ISO_8859_1));
bw.write("GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: close\r\n\r\n");
bw.flush(); // 必須 flush 將緩衝區數據推入網路

// 接收響應
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
String line;
while ((line = br.readLine()) != null) {
    System.out.println(line);
}
socket.close();
關鍵細節:
  • flush() 觸發實際發送,預設 8KB 緩衝區。
  • HTTP 協議頭以 \r\n\r\n 結束,缺少會導致服務端持續等待,客戶端 readLine() 阻塞至超時。
  • 字符編碼需雙端一致,HTTP 頭通常用 ISO-8859-1,Body 視 Content-Type 而定。

三、關閉網路連接 — 4 種方式與狀態檢測

觸發關閉的 4 種途徑

  1. socket.close() — 顯式關閉,**推薦**
  2. 關閉 InputStream 或 OutputStream — 流關閉導致 Socket 關閉
  3. 程序退出 — JVM 關閉時由系統回收
  4. Socket 對象失去引用 — GC 回收時由 finalize() 關閉(不可靠,**不推薦**)

前兩種屬於主動可控關閉,後兩種屬於被動不可控,生產環境務必使用方式 1 或 2。

連接狀態檢測:isClosed() vs isConnected()

方法含義關閉後未連接連接中
isClosed()對象是否處於關閉狀態truefalse (未調用 close) / true (調用 close)false
isConnected()是否曾經連接成功truefalsetrue

判斷當前是否連接中:!socket.isClosed() && socket.isConnected()

演示代碼與輸出

Socket s1 = new Socket("www.example.com", 80);
printState(s1, "s1"); // closed=false, connected=true -> 連接中

s1.getOutputStream().close(); // 關閉流導致 Socket 關閉
printState(s1, "s1"); // closed=true, connected=true -> 非連接中

Socket s2 = new Socket();
printState(s2, "s2"); // closed=false, connected=false -> 非連接中

s2.close(); // 未連接的 Socket 必須顯式 close 才能使 isClosed=true
printState(s2, "s2"); // closed=true, connected=false
s1.isClosed():false
s1.isConnected():true
s1 處於連接狀態!
s1.isClosed():true
s1.isConnected():true
s1 處於非連接狀態!
s2.isClosed():false
s2.isConnected():false
s2 處於非連接狀態!
s2.isClosed():true
s2.isConnected():false
s2 處於非連接狀態!

半關閉:shutdownInput() / shutdownOutput()

僅關閉單向流,**不關閉 Socket**,適合雙工協議結束單向傳輸:

Socket s = new Socket("host", 80);
s.shutdownInput();  // 不再接收,發送仍可用
s.shutdownOutput(); // 不再發送,接收仍可用

// 狀態檢測
s.isInputShutdown();  // true
s.isOutputShutdown(); // true
s.isClosed();         // false (Socket 仍開啟)

最佳實踐總結

  • 使用 try-with-resources (Java 7+) 自動關閉 Socket 與流。
  • 發送數據後必須 flush()。
  • 協議設計明確邊界(如 \r\n\r\n、Content-Length、分隔符),避免 read() 無限阻塞。
  • 長連接場景定期發送心跳,檢測對端存活。
  • 異常分支中確保資源釋放,避免文件描述符洩漏。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved