【问题标题】:Can't read from socket (hangs)无法从套接字读取(挂起)
【发布时间】:2011-05-07 11:07:59
【问题描述】:

我正在使用 PHP 连接到本地 C++ 套接字服务器,以保持 Web 应用程序和几个守护程序之间的状态。我可以向套接字服务器发送数据,但不能从它接收;它只是阻塞socket_read() 并无限期挂起。我是否忘记了一些愚蠢的事情(如 NULL 字符或换行符的不同组合)? PHP 如下:

socket_connect($sock, $addr, $port); 
socket_write($sock, 'Hello world');
$str = '';
while($resp = socket_read($sock, 1000))
    $str .= $resp;
socket_close($sock);
die("Server said: {$str}");

socket服务器的相关部分如下(注意<<>>操作符重载):

std::string data;
sock >> data;
sock << data << std::endl;

&gt;&gt; 调用 Socket::recv(std::string&amp;)&gt;&gt; 调用 Socket::send(const std::string&amp;)

这在(例如)telnet 上工作得很好,但 PHP 不想玩得很好。感谢您提出任何想法/建议。

【问题讨论】:

  • 您在 C++ 中使用什么套接字库,或者您是否创建了自己的(*nix 或 Windows)?另外,错误是什么?它只是永远阻塞 sock >> 数据还是发生了更多事情?附加说明;我已经有一段时间没有使用 PHP 与 C++ 服务器进行通信了,但是您的服务器是否向套接字发送了一个终止,所以它告诉 PHP 停止读取?您的循环将在 PHP 中连续读取,直到服务器告诉它套接字已完成发送信息;请注意,这不是实时更新(因为它会在数据完全收集并存储在您的 var 中后打印数据)。
  • 这可能只是您的代码中缺少的,但您是否先socket_create $sock?
  • 另外,我在您的代码中没有看到这一点,但您需要在循环完成时打印 $str。
  • 哎呀,我不小心从我的代码中遗漏了socket_create() - 上面已更新。我应该早点澄清一下,抱歉 - 这是在 *NIX 上的。本身没有任何错误;服务器本身工作正常(我可以 telnet 并且它按预期工作) - 它是 PHP 的 socket_read() 无限期挂起。套接字服务器只是我拼凑起来的一个临时服务器,因为我找不到可以使用的好服务器(任何建议都会很棒)。

标签: php c++ sockets


【解决方案1】:

与大多数编程语言一样,PHP 中的套接字默认以阻塞模式打开,除非使用socket_set_nonblock 另行设置。

这意味着除非发生超时/错误或接收到数据,否则socket_read 将永远挂在那里。

由于您的终止字符似乎是一个新行,请尝试:

while($resp = socket_read($sock, 1000)) {
   $str .= $resp;
   if (strpos($str, "\n") !== false) break;
}
socket_close($sock);
die("Server said: $str");

【讨论】:

  • 确实,我知道默认情况下它们会阻塞,但我完全忘记了 a) 在写入响应后关闭套接字或 b) 中断循环。感谢那。但是,如果我切换它,使用 NULL 字符似乎不起作用(理想情况下它不会是换行符) - 如果我在sock &lt;&lt; data &lt;&lt; "\0" 之后尝试strpos($str, "\0"),它不会捕捉到它。有什么想法吗?
  • 与 C 或 C++ 不同,PHP 不会将空字节放在字符串的末尾,这就是为什么 strpos\0 在测试时会产生布尔值 false。您必须提供另一个分隔符,使用超时(请参阅How to set a timeout on socket read?),或从服务器端关闭套接字。
  • 很公平 - 通过我的测试也意识到了这一点。感谢您的帮助!
  • 我认为只在 $resp 中搜索可以稍微改进代码:if (strpos($resp, "\n") !== false) break;
【解决方案2】:

TCP 套接字通常会尝试将多个小型发送调用组合到一个数据包中,以避免发送太多数据包 (Nagle's algorithm)。这可能是在 send() 调用之后您无法收到任何内容的原因。您必须使用TCP_NODELAY 打开套接字以避免这种情况。

【讨论】:

    【解决方案3】:

    你也可以在从socket读取的时候试试这个:

    if(socket_recv ( $socket , $response , 2048 , MSG_PEEK)!==false)
    {
       echo "Server said: $response";
    }
    

    注意:使用 MSG_WAITALL 代替 MSG_PEEK 可能会导致套接字挂起(根据我曾经的一次经历)。

    【讨论】:

      【解决方案4】:

      我实际上只是修复了一个与此类似的问题,除了管道 (Is php's fopen incompatible with the POSIX open for pipes)。我不知道解决方案在多大程度上会相似,但也许值得一看?我同意 RageD 的观点,即您应该在读取和写入行之间添加一个 cout,以测试并查看 C++ 是否挂在 sock >> 数据线或 sock

      【讨论】:

        猜你喜欢
        • 2015-01-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-14
        • 2016-09-06
        • 1970-01-01
        相关资源
        最近更新 更多