在使用 PHP 编写基于 Socket 的服务器应用时,socket_accept() 是接收客户端连接的关键函数。然而在某些场景中,即便程序逻辑正确,也可能遇到 socket_accept() 无法正常接收连接的情况。本文将介绍如何使用 strace 和 netstat 工具,快速定位和解决这一问题。
以下是一个简单的 PHP 服务端程序,监听 8080 端口:
<?php
$host = '0.0.0.0';
$port = 8080;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, $host, $port);
socket_listen($socket);
echo "Server listening on $host:$port\n";
while (true) {
$client = socket_accept($socket);
if ($client === false) {
echo "Failed to accept connection\n";
continue;
}
$msg = "Hello from server\n";
socket_write($client, $msg, strlen($msg));
socket_close($client);
}
?>
当你发现 socket_accept() 卡住,或者始终返回 false 时,就需要借助系统工具来深入排查。
strace 是一个 Linux 下的系统调用追踪工具,可以直接查看 PHP 脚本在运行时调用了哪些系统底层函数,以及它们的执行结果。
找到正在运行的 PHP 进程 PID,例如使用 ps:
ps aux | grep php
使用 strace 附加到该进程:
strace -p <PID> -e trace=network
观察输出中是否存在如下行为:
accept(...) = -1:系统调用失败,通常伴随错误码(如 EAGAIN、ECONNABORTED 等)
listen(...) 或 bind(...) 报错:可能意味着端口被占用或权限不足
没有调用 accept():程序可能卡在其他地方
如果 strace 显示 accept() 正常,但仍然无连接接入,那么问题可能出在网络层或防火墙。
netstat 和 ss 可用于检查端口是否处于监听状态,以及是否已有连接建立。
netstat -tlnp | grep :8080
或:
ss -tlnp | grep :8080
你应该能看到类似以下的输出:
tcp LISTEN 0 128 0.0.0.0:8080 0.0.0.0:* users:(("php",pid=12345,fd=3))
如果没有任何监听,说明 socket_bind() 或 socket_listen() 失败;
如果监听正常但无连接,可能是客户端未发起连接,或被防火墙拦截;
如果状态为 CLOSE_WAIT 或 TIME_WAIT,说明连接生命周期异常结束。
检查错误信息
每次 Socket 调用后使用 socket_strerror(socket_last_error()) 查看具体错误信息。
防火墙或 SELinux 限制
使用 iptables 或 firewalld 确认 8080 端口未被阻塞。
端口被其他程序占用
使用 lsof -i :8080 检查是否有其他程序占用了该端口。