在 PHP 中实现一个基于 Socket 的阻塞式服务器,可以帮助我们理解更底层的网络通信原理。阻塞模式意味着服务器会在读取或写入数据前一直等待,直到有数据可以处理或连接已准备好。本文将一步步解析如何使用 PHP 的 socket 扩展,从创建 socket 到设置为阻塞模式,并构建一个简单的阻塞式服务器。
首先,确认 PHP 环境中启用了 socket 扩展。在 php.ini 中查找并取消下面这一行的注释(如果有):
extension=sockets
使用 php -m 命令检查扩展是否启用:
php -m | grep sockets
如果能看到 sockets,说明扩展已启用。
以下是创建 socket 的基本步骤,并逐步介绍每个函数的作用。
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
die("socket_create() failed: " . socket_strerror(socket_last_error()) . "\n");
}
这里我们创建的是一个基于 IPv4(AF_INET)的 TCP socket(SOCK_STREAM)。
可以设置一些 socket 选项,如端口复用:
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
$host = '0.0.0.0';
$port = 9000;
if (!socket_bind($socket, $host, $port)) {
die("socket_bind() failed: " . socket_strerror(socket_last_error($socket)) . "\n");
}
if (!socket_listen($socket, 5)) {
die("socket_listen() failed: " . socket_strerror(socket_last_error($socket)) . "\n");
}
5 表示最多挂起的连接请求数。
使用 socket_set_block 将 socket 设置为阻塞模式。
if (!socket_set_block($socket)) {
die("socket_set_block() failed: " . socket_strerror(socket_last_error($socket)) . "\n");
}
一旦设置为阻塞模式,后续的 socket_accept、socket_read 等函数都会阻塞,直到操作完成。
echo "服务器已启动,监听端口 $port...\n";
while (true) {
$clientSocket = socket_accept($socket);
if ($clientSocket === false) {
echo "socket_accept() failed: " . socket_strerror(socket_last_error($socket)) . "\n";
continue;
}
$input = socket_read($clientSocket, 1024);
if ($input === false) {
echo "socket_read() failed: " . socket_strerror(socket_last_error($clientSocket)) . "\n";
} else {
$input = trim($input);
echo "收到消息: $input\n";
$response = "你发送了:$input\n";
socket_write($clientSocket, $response, strlen($response));
}
socket_close($clientSocket);
}
在需要退出时,别忘了关闭主 socket:
socket_close($socket);
你可以使用命令行客户端如 telnet 连接测试:
telnet gitbox.net 9000
或写一个简单的 PHP 客户端进行连接测试。
通过 socket_create 到 socket_set_block,我们构建了一个完整的阻塞式服务器。这个模式适合简单的网络通信应用,比如教学、调试或低并发场景。对于高并发场景,建议使用非阻塞模式或搭配 select/poll 实现更高效的事件循环。
通过本文的实践步骤,你可以掌握在 PHP 中构建阻塞式服务器的全过程,并为进一步深入网络编程打下坚实基础。