如何在 PHP 中优雅地关闭本地套接字连接...?

3

我有一个使用PHP编写的客户端,通过纯文本Unix域套接字连接到本地C服务器程序。然而,唯一能够使其正常关闭的方式是在结束套接字会话之后执行以下操作:

stream_socket_shutdown($sock, 1); // shutdown socket writing
usleep(500 * 1000);           // wait for 0.5 seconds
fclose($sock);                // actually close the socket (finally)

我希望能像我的C客户端一样优雅地关闭它:

shutdown(connection_fd, SHUT_WR);     // tell server we've stopped sending queries
// Read from the socket until poll(connection_fd) yields POLLRDHUP / POLLHUP / POLLERR / POLLNVAL
shutdown(connection_fd, SHUT_RDWR);   // tell connection we've stopped listening
close(connection_fd);                 // close the whole connection (finally)

然而,PHP似乎没有直接的socket poll()等价物。我该如何让PHP优雅地关闭我的本地socket?

1个回答

5

你尝试过使用stream_set_blocking()吗?如果将流设置为非阻塞,你应该能够在关闭连接之前消耗所有挂起的数据:

socket_shutdown($sock, STREAM_SHUT_WR); 
stream_set_blocking($sock, 0);
while( fgets($sock) !== false ) { ; }
fclose($sock);

编辑:使用常量,并修复了设置阻塞命令使用正确值的问题。

解释:通过将流设置为非阻塞,然后从中读取所有内容,我们消耗了输入缓冲区中已有的所有数据。这大大加快了关闭过程,通过向对等方发出信号表明我们的缓冲区已清除,他可以向我们发送更多消息(如FIN包),从而使关闭过程更加顺畅。

仍然有可能出现缓慢的关闭,但可能性小得多。


啊哈 - 你99%正确。如果你把第一行改成 'socket_shutdown($sock, 0);'(这就是你的意思 :-) ),那么它似乎确实可以优雅地关闭单个连接。然而,当有多个连接处于活动状态时,我不确定这是否会按预期工作 - 我不想让我的PHP主动旋转等待。所以也许这不是完整的答案...你同意吗? - Nick Pelling
它实际上不会自旋等待,因为套接字处于非阻塞模式。相反,将读取所有挂起的数据,而无需等待另一端发送更多数据,这几乎在所有情况下都加快了关闭操作。 - slashingweapon
我完全同意已经发送的所有数据都会到达。但是,如果服务器需要从(例如)附加的I2C设备获取一些数据才能响应,则客户端发送查询和服务器响应之间可能会有显着延迟。我猜在这些(频繁的)情况下,PHP将自旋等待,如果可能的话,我宁愿避免这种情况。我猜最终我会构建一组非阻塞fgets()调用和usleep()来解决这个问题。 - Nick Pelling
是的,它并不完美。但比以前更好了。 - slashingweapon
非常感谢您的帮助,看起来我现在可以构建一个答案了。 :-) - Nick Pelling

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接