检测与 PHP 套接字模块的对等断开连接 (EOF)

Detecting peer disconnect (EOF) with PHP socket module

提问人:i336_ 提问时间:9/9/2016 更新时间:9/16/2016 访问量:2509

问:

我在使用 PHP 的套接字库时遇到了一个奇怪的问题:我似乎无法检测/区分服务器 EOF,结果我的代码无助地进入了无限循环。

下文作进一步解释;首先,一些背景(这里没有什么特别花哨的事情):

<?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";

}

上面的代码只是连接到服务器并打印它读取的内容。这是我正在编写的小型套接字库中的精简版本。

为了进行测试,我目前正在使用绑定套接字并成为服务器。运行后,我可以启动上面的代码,它连接并工作 - 例如,我可以在窗口中输入,PHP 接收它。(从PHP发送数据也可以,但是我排除了该代码,因为它不相关。ncat -vvklp 8081ncat

然而,当 I ^C ncat 的那一刻,上面的代码进入了一个硬无限循环 - PHP 说套接字上没有错误。

我试图弄清楚按钮在哪里,它使PHP颠倒在头上,并使其意识到对等方已断开连接。

  • socket_get_status()是一个很大的用词不当 - 它是 的别名,它实际上不适用于套接字!stream_get_meta_data()

  • feof()类似的喷口.Warning: feof(): supplied resource is not a valid stream resource

我找不到用于检测对等 EOF 的函数。socket_*

socket_read() 的 PHP 手册之一最初劝阻我不要使用该函数,所以我改用了该函数,但我最终尝试了它以防万一 - 但没有骰子;切换接收呼叫不起作用。socket_recv()

我发现观察套接字进行写入,然后尝试写入它会突然让 PHP 去“哦,等等,对”并开始返回 Broken pipe - 但我对写入服务器不感兴趣,我想从中读取!

最后,关于注释部分 - 我愿意使用 PHP 的内置流功能,但这些函数没有提供任何处理异步连接事件的方法(我想这样做,因为我正在建立多个连接)。我可以做,但无法找出何时建立连接(6yo PHP bug #52811)。stream_*stream_socket_client(... STREAM_CLIENT_ASYNC_CONNECT ...)

PHP 套接字 EOF

评论

0赞 Ryan Vincent 9/9/2016
这不是 TCP/IP 套接字的工作方式。没有“自然”的方法来检测链接的另一端是否已经“消失”。这些不是文件读/写。这是通过“希望它到达那里”或“希望得到一些东西”传递的信息:)考虑到这一点,有一些常见的技术可以检测出“客户端消失了”。最简单的方法是在协议中设计一些东西,用于与客户端“交换数据”。我使用“ping 请求”。短时间内没有回复 - 它已经消失了。
0赞 Ryan Vincent 9/9/2016
一些方法...也许很有趣?这些也适用于 PHP...stackoverflow.com/questions/22860969/....另外,stackoverflow.com/questions/283375/...,PHP:stackoverflow.com/questions/11451840/...
0赞 i336_ 9/9/2016
我明白你在说什么,并感谢这些参考资料。请注意,当我的代码失败并陷入无限循环时,该无限循环包括读取操作,并且在读取操作之后,套接字函数报告“成功”(并永远继续循环)。出于这个原因,我怀疑我正在研究 PHP 中可以说是错误的行为。
0赞 i336_ 9/9/2016
哦。所以在正常情况下我永远不会得到 0 字节的数据?真的是这样吗?
0赞 Ryan Vincent 9/9/2016
有用?好的,很聪明,会检查客户端是否仍然存在。它将等到它有有用的信息返回。1) 您必须检查语句中的错误返回。目前,您正在处理它,文件读取。请不要。2) 如果没有错误,则始终检查返回的数据长度 - 零表示客户端已“消失”。通过这样做,您应该从代码中获得更有用的结果。我倾向于使用大约 60 秒的长超时。socket_selectsocket_select

答:

1赞 i336_ 9/16/2016 #1

好吧,我想我不妨把上面的评论变成答案。所有的功劳都归功于瑞安·文森特(Ryan Vincent)帮助我的厚实的头脑弄清楚了这一点:)

socket_recv如果对等体已断开连接,或者发生任何其他网络错误,将特别返回。0FALSE

作为参考,在 C 中,的返回值是您刚刚收到的新数据的长度(可以是 ),或者表示错误条件(其值可以在 中找到)。recv()0-1errno

用来表示错误条件(并且只是一种任意类型的错误条件)不是 PHP 的标准和唯一错误方式。其他网络库不以这种方式工作。0

你需要这样处理它。

$r = socket_recv($socket, $buf, $len);

if ($r === FALSE) {

   // Find out what just happened with socket_last_error()
   // (there's a great list of error codes in the comments at
   // http://php.net/socket_last_error - considering/researching
   // the ramifications of each condition is recommended)

} elseif ($r === 0) {

   // The peer closed the connection. You need to handle this
   // condition and clean up.

} else {

   // You DO have data at this point.
   // While unlikely, it's possible the remote peer has
   // sent you data of 0 length; remember to use strlen($buf).

}