我在使用PHP的sockets库时遇到了一个奇怪的问题:似乎无法检测/区分服务器的EOF,因此我的代码陷入了无限循环。
上面的代码只是连接到服务器并打印读取的内容。这是我正在编写的小套接字库中的简化版本。
为了测试,我目前使用“ncat -vvklp 8081”来绑定套接字并充当服务器。运行它后,我可以启动上面的代码,它会连接并工作 - 例如,我可以在“ncat”窗口中输入,PHP会接收它。(从PHP发送数据也可以工作,但我已经排除了那部分代码,因为它不相关。)
然而,一旦我^C ncat,上面的代码就进入了一个硬无限循环 - 而PHP说套接字没有错误。
我正在尝试找出让PHP意识到对等方已断开连接的按钮在哪里。
socket_get_status()是一个很大的误称 - 它是stream_get_meta_data()的别名,并且它实际上不适用于套接字!feof()同样会输出“Warning: feof(): supplied resource is not a valid stream resource”。
我找不到用于检测对等方EOF的
我已经发现,观察套接字的写入情况,然后尝试向其写入将突然使PHP“哦,等等,对了”并开始返回
以下是更详细的说明;首先,一些背景信息(这里没有什么特别复杂的东西):
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, '127.0.0.1', 8081);
for (;;) {
$read = [$socket];
$except = NULL;
$write = [];
print "Select <";
$n = socket_select($read, $write, $except, NULL);
print ">\n";
if (count($read)) {
print "New data: ";
#socket_recv($socket, $data, 1024, NULL);
$data = socket_read($socket, 1024);
print $data."\n";
}
print "Socket status: ".socket_strerror(socket_last_error())."\n";
}
上面的代码只是连接到服务器并打印读取的内容。这是我正在编写的小套接字库中的简化版本。
为了测试,我目前使用“ncat -vvklp 8081”来绑定套接字并充当服务器。运行它后,我可以启动上面的代码,它会连接并工作 - 例如,我可以在“ncat”窗口中输入,PHP会接收它。(从PHP发送数据也可以工作,但我已经排除了那部分代码,因为它不相关。)
然而,一旦我^C ncat,上面的代码就进入了一个硬无限循环 - 而PHP说套接字没有错误。
我正在尝试找出让PHP意识到对等方已断开连接的按钮在哪里。
socket_get_status()是一个很大的误称 - 它是stream_get_meta_data()的别名,并且它实际上不适用于套接字!feof()同样会输出“Warning: feof(): supplied resource is not a valid stream resource”。
我找不到用于检测对等方EOF的
socket_*
函数。
PHP手册中socket_read()
的注释之一最初让我不想使用该函数,因此我改用了socket_recv()
,但最终还是试了一下 - 但没有任何效果;切换接收调用没有影响。我已经发现,观察套接字的写入情况,然后尝试向其写入将突然使PHP“哦,等等,对了”并开始返回
Broken pipe
- 但我不想向服务器写入,我想从它读取!
最后,关于被注释的部分 - 我更希望使用PHP内置的流功能,但是stream_*
函数不提供处理异步连接事件的任何方法(我想这样做,因为我正在建立多个连接)。我可以使用stream_socket_client(...STREAM_CLIENT_ASYNC_CONNECT...)
但是无法找出何时建立连接(6yo PHP bug #52811)。
socket_select
很聪明,会检查客户端是否还在那里。它会等待直到有有用的信息返回。1)你必须检查socket_select
语句的错误返回。目前你把它当作文件读取处理,请不要这样做。2)如果没有错误,总是检查返回的数据长度-零意味着客户端“离开了”。通过这样做,您应该能够从代码中获得更有用的结果。我倾向于使用长达60秒的超时时间。 - Ryan Vincentsocket_recv()
函数显然会将返回缓冲区设置为 NULL "如果出现错误、连接被重置或者没有可用数据"(这很有帮助),而socket_read()
函数则说当“远程主机关闭连接”时返回 FALSE,同时也表示在“没有更多可读数据”时返回一个空字符串。 - i336_