\r\n (CRLF) 結束錯誤做法:在 Servlet.init() 中直接 while(true) server.accept() → 導致 Tomcat 啟動超時。
正確做法:init() 中啟動獨立線程,線程內循環 accept(),每個連接再派生工作線程處理。
@WebServlet(name = "SocketServlet", loadOnStartup = 0)
public class SocketServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
new Thread(() -> {
try (ServerSocket server = new ServerSocket(4700)) {
while (true) {
Socket socket = server.accept();
new SocketHandler(socket).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
class SocketHandler extends Thread {
private final Socket socket;
SocketHandler(Socket s) { this.socket = s; }
@Override
public void run() {
try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
String line = in.readLine(); // 阻塞直到 \r\n
System.out.println("Received: " + line);
out.println("server send"); // 自動 flush + \r\n
} catch (IOException e) {
e.printStackTrace();
} finally {
try { socket.close(); } catch (IOException ignored) {}
}
}
}
<?php
$userName = 'veverrr';
$password = 'SWJTU';
$host = 'localhost';
$port = 4700;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (!$socket) die("socket_create failed: " . socket_strerror(socket_last_error()));
$conn = @socket_connect($socket, $host, $port);
if (!$conn) die("socket_connect failed: " . socket_strerror(socket_last_error($socket)));
// 關鍵:發送數據必須以 \r\n 結束,對應 Java readLine()
$payload = $userName . $password . "\r\n";
socket_write($socket, $payload, strlen($payload));
$response = socket_read($socket, 1024);
echo "Server reply: $response";
socket_close($socket);
| 問題 | 原因 | 解決 |
|---|---|---|
Java readLine() 永久阻塞 | PHP 發送缺少 \r\n | PHP 端拼接 chr(13).chr(10) 或 "\r\n" |
| Java 無收到數據 | PrintWriter 未 flush() | 構造 new PrintWriter(out, true) 自動行刷新 |
| Tomcat 啟動卡死 | 主線程阻塞於 accept() | 改為獨立線程監聽 |
| 中文亂碼 | 編碼不一致 | 雙方統一指定 UTF-8 的 InputStreamReader/OutputStreamWriter |
ExecutorService) 替代無限創建線程。