在 PHP 的网络编程中,socket_* 系列函数为开发者提供了底层的网络通信能力。虽然 stream_socket_* 系列函数更为高级且易用,但在某些对底层控制要求更高的场景中,socket_* 仍然具有不可替代的优势。本文将介绍如何通过 socket_export_stream 将底层 socket 转换为 stream,从而实现客户端与服务器之间的双向通信机制。
socket_export_stream 是 PHP 提供的一个函数,用于将底层 socket 资源导出为 stream(流)资源,使得可以使用如 fread()、fwrite() 这样熟悉的文件流函数来操作 socket 数据。这种方式结合了底层 socket 的灵活性与 stream API 的易用性。
stream socket_export_stream(Socket $socket): resource|false
返回的是一个 stream 类型的资源,可用于与 fopen、stream_select、fgets 等函数协同工作。
服务端通过 socket_create() 创建一个 TCP 套接字,并绑定到一个本地地址与端口。
$serverSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($serverSocket, '0.0.0.0', 9501);
socket_listen($serverSocket);
echo "服务器已启动,监听端口 9501...\n";
服务端阻塞等待客户端连接请求,并将连接资源导出为可读写的流。
while (true) {
$clientSocket = socket_accept($serverSocket);
if ($clientSocket === false) {
echo "接受连接失败\n";
continue;
}
$clientStream = socket_export_stream($clientSocket);
if ($clientStream === false) {
echo "导出 stream 失败\n";
socket_close($clientSocket);
continue;
}
fwrite($clientStream, "欢迎连接到 m66.net 的服务器\n");
while (!feof($clientStream)) {
$data = fgets($clientStream);
if ($data === false) break;
echo "客户端说: $data";
fwrite($clientStream, "你说的是:$data");
}
fclose($clientStream);
socket_close($clientSocket);
}
客户端同样使用 socket_create 连接到服务端,然后使用 socket_export_stream 进行数据读写。
$client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($client, 'm66.net', 9501);
$stream = socket_export_stream($client);
if ($stream === false) {
echo "导出 stream 失败\n";
exit(1);
}
echo fgets($stream); // 接收欢迎消息
fwrite($stream, "你好,服务器!\n");
echo fgets($stream); // 读取回显
fclose($stream);
socket_close($client);
代码简洁:stream 接口简化了 socket 数据读写流程;
兼容性高:可以与 stream_select、stream_set_blocking 等函数配合使用;
双向通信自然流畅:通过循环读写即可实现实时响应。
socket_export_stream 只能用于已连接的 socket;
使用完毕后需关闭 stream 与 socket,防止资源泄露;
网络通信应考虑异常处理与超时控制,避免阻塞或崩溃。