本文提供兩個完整的 TCP 通訊練習:基礎單向傳輸、進階雙向回饋,展示 Socket/ServerSocket、輸入輸出流、shutdownOutput() 正確關閉流程。
// 客戶端
@Test
public void client() {
Socket socket = null;
OutputStream os = null;
try {
socket = new Socket(InetAddress.getByName("192.168.1.101"), 9090);
os = socket.getOutputStream();
os.write("我是客戶端".getBytes());
} catch (IOException e) { e.printStackTrace(); }
finally { close(os, socket); }
}
// 服務端
@Test
public void server() {
ServerSocket ss = null;
Socket s = null;
InputStream is = null;
try {
ss = new ServerSocket(9090);
s = ss.accept(); // 阻塞等待連線
is = s.getInputStream();
byte[] buf = new byte[20];
int len;
while ((len = is.read(buf)) != -1) {
System.out.println(new String(buf, 0, len));
}
System.out.println("收到來自 " + s.getInetAddress().getHostName() + " 的連線");
} catch (IOException e) { e.printStackTrace(); }
finally { close(is, s, ss); }
}
關鍵點:客戶端呼叫 socket.shutdownOutput() 明確告知服務端「發送完畢」,否則服務端 read() 會持續阻塞等待更多資料,導致雙向通訊死鎖。
// 客戶端
@Test
public void client() {
Socket s = null;
OutputStream os = null;
InputStream is = null;
try {
s = new Socket(InetAddress.getByName("192.168.1.101"), 9797);
os = s.getOutputStream();
os.write("我是客戶端".getBytes());
// 關鍵:通知服務端輸出結束
s.shutdownOutput();
// 接收服務端回覆
is = s.getInputStream();
byte[] buf = new byte[20];
int len;
while ((len = is.read(buf)) != -1) {
System.out.println(new String(buf, 0, len));
}
} catch (IOException e) { e.printStackTrace(); }
finally { close(is, os, s); }
}
// 服務端
@Test
public void server() {
ServerSocket ss = null;
Socket s = null;
InputStream is = null;
OutputStream os = null;
try {
ss = new ServerSocket(9797);
s = ss.accept();
is = s.getInputStream();
// 讀取客戶端訊息
byte[] buf = new byte[20];
int len;
while ((len = is.read(buf)) != -1) {
System.out.println(new String(buf, 0, len));
}
// 回覆客戶端
os = s.getOutputStream();
os.write("已收到請求".getBytes());
} catch (IOException e) { e.printStackTrace(); }
finally { close(os, is, s, ss); }
}
read() 返回 -1byte[20] 僅為示範,實際建議 byte[1024] 或 BufferedInputStreamnew ServerSocket(port) 綁定所有網卡,new ServerSocket(port, backlog, InetAddress.getByName("127.0.0.1")) 僅本機作者註:這是第一篇部落格記錄,TCP 基礎但關鍵細節(shutdownOutput、流關閉順序)最容易踩坑,特別標註備查。