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

i33*_*36_ 5 php sockets eof

我对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";

}
Run Code Online (Sandbox Code Playgroud)

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

为了测试,我目前正在使用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.

我找不到socket_*用于检测对等EOF 的功能.

PHP手册中的一个注释socket_read()最初阻止我使用该功能,所以我使用了socket_recv(),但我最终尝试了以防万一 - 但没有骰子; 切换接收呼叫无效.

已经发现,看写作的插座,然后再试图写它会突然让PHP去"哦,等等,右侧",并开始返回Broken pipe-但是我不感兴趣,以书面形式向服务器,我想从中读!

最后,关于评论的部分-我远远更喜欢使用PHP的内置流功能,但该stream_*功能不提供用于处理异步连接事件(这是我想做的事情,因为我在做多个连接)的任何手段.我可以做,stream_socket_client(... STREAM_CLIENT_ASYNC_CONNECT ...)但后来无法找到连接建立的时间(6yo PHP bug#52811).

i33*_*36_ 1

好吧,我想我不妨把上面的评论变成一个答案。所有的功劳都归功于 Ryan Vincent,他帮助我的头脑解决了这个问题:)

socket_recv0如果对等方已断开连接,或者FALSE如果任何其他网络错误,

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

使用0来指示错误条件(并且只是一种任意类型的错误条件)不是标准的,并且以所有错误的方式对于 PHP 来说是唯一的。其他网络库不以这种方式工作。

你需要这样处理。

$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).

}
Run Code Online (Sandbox Code Playgroud)