Socket 通信中,緩存流 BufferedOutputStream 預設 8KB 緩衝區,**數據不會立即發送**,直到:
flush()close() (內部會 flush)若客戶端寫入後不 flush(),服務端 read() 會阻塞等待數據,雙方陷入「死等」狀態,控制台無任何輸出,也無異常拋出。
Socket socket = new Socket("127.0.0.1", 8888);
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
bos.write("Hello Server\n".getBytes(StandardCharsets.UTF_8));
bos.flush(); // ★ 關鍵:強制發送緩衝區數據
// 或使用 PrintWriter 自動行刷新
PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true); // true = autoFlush
pw.println("Hello Server"); // 自帶 \n 觸發 flush
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
String line;
while ((line = br.readLine()) != null) { // 遇到 \n/\r\n 返回
System.out.println("收到: " + line);
}
// Server.java
public class Server {
public static void main(String[] args) throws IOException {
try (ServerSocket ss = new ServerSocket(8888)) {
System.out.println("Server started...");
while (true) {
try (Socket s = ss.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(s.getOutputStream(), "UTF-8"), true)) {
String msg = br.readLine();
System.out.println("Client: " + msg);
pw.println("Echo: " + msg); // autoFlush 發送
}
}
}
}
}
// Client.java
public class Client {
public static void main(String[] args) throws IOException {
try (Socket s = new Socket("127.0.0.1", 8888);
PrintWriter pw = new PrintWriter(new OutputStreamWriter(s.getOutputStream(), "UTF-8"), true);
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
BufferedReader console = new BufferedReader(new InputStreamReader(System.in))) {
String input;
while ((input = console.readLine()) != null) {
pw.println(input); // 發送
String reply = br.readLine(); // 接收回显
System.out.println("Server: " + reply);
}
}
}
}
| 錯誤 | 後果 | 修正 |
|---|---|---|
| 只用 BufferedOutputStream 不 flush | 數據卡在緩衝區,對端收不到 | 每次寫完調用 flush() |
用 read(byte[]) 且無協議分隔 | 粘包/半包,業務解析錯誤 | 按行/定長/長度前綴協議 + BufferedReader.readLine() |
| 字符集不一致 | 中文亂碼 | 雙方統一 StandardCharsets.UTF_8 |
| 未關閉流/Socket | 資源洩漏、連接不釋放 | try-with-resources 自動關閉 |
「緩存流必須配合 flush/協議使用」是 Java 網絡編程的鐵律,掌握它能省去無數調試夜晚。