【问题标题】:PHP socket_read() only gets the first byte of the streamPHP socket_read() 只获取流的第一个字节
【发布时间】:2011-12-04 11:08:33
【问题描述】:

在 linux 中使用 socket_read() 时,我遇到了一个非常奇怪的行为。

我正在使用 2048 缓冲区限制的 socket_read。

在我的 Windows 系统上,它获得整个响应,而在我的 Linux 服务器上,它只获得响应的第一个字节。

$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (!socket_connect($sock, 'my-server.dyndns.org', 8888)) {
   die('no connect');
}

$req = 'request';
socket_write($sock, $req);

if (false !== ($buf = socket_read($sock, 2048)) {
    echo $buf; // This only contains the first byte of the response.
}

socket_close($sock);

如果我再次调用 socket_read() 它会得到字符串的其余部分:

// This works: 

while((false !== ($buf = socket_read($sock, 2048)))) {
    echo "Read ".strlen($buf)." bytes from socket_read().\n";
    if ($buf == "") break;
    $b .= $buf;
    sleep(1);
}

/* Output: 
 *
 * Read 1 bytes from socket_read().
 * Read 307 bytes from socket_read().
 * Read 0 bytes from socket_read().
 * Done.
 */ 

如果我在调用 socket_read() 之前等待 2 秒,我也会得到一个有效的响应:

// This also works:

sleep(2);
if (false !== ($buf = socket_read($sock, 2048)) {
    echo $buf; // all 308 bytes are read correctly.
}

socket_read() 不应该等待缓冲区变满,还是等待字符串结束?

我做错了什么?

【问题讨论】:

  • 你的问题是什么?您是否想知道为什么在写入后需要等待一段时间才能从套接字读取内容?
  • 问题是我不能相信上述解决方案。我不能等待任意时间并假设响应是有效的。另一个解决方案有类似的问题:如果输出是分块发送的,我可能会在所有读取完成之前中断读取循环。
  • 嗯,你总是需要等待响应。您可以检查是否有数据,如果没有,请再试一次(一次又一次)。自己构建缓冲区,同时解析和处理套接字错误。
  • @hakre 我无法确定输出何时完成。
  • 当然,这就是您需要等待的原因。是网络,你需要检查是否有东西可以阅读。

标签: php linux sockets


【解决方案1】:

众所周知,Linux 上的网络堆栈比 Windows 上的更好(更快)(这是主观的,我们只是说:它的行为不同)。另一方面,这意味着您的代码也需要处理这些差异。

套接字不是实时的,而是异步的。因此,如果您向套接字写入某些内容,则不能指望您已经可以读取它。您需要给远程守护进程一些时间来实际响应您的写入。

只有当远程端确实向您的套接字写入了一些内容并且网络已经传输了该数据时,您才能读取它。

【讨论】:

  • 手册建议socket_read将在读取指定的缓冲区长度或读取流终止字符时返回。因此,当我调用它时,我假设我正在等待远程守护进程。尽管如此,它似乎会立即返回缓冲区中当前的任何内容。
  • 使用php.net/manual/en/function.socket-recv.php + MSG_WAITALL 标志您可能拥有更多控制权。试试看。
  • 不,不是“知道 linux 网络堆栈是‘更好’还是‘更快’”。这只是旋转。可能是,但你不能假设它是。
  • @MarkR:实际上这是主观的,对。例如,什么是“更好”? “更快”是什么意思?所以你完全正确,这只是我(和我认识的许多其他人)的看法。但我不会称之为旋转,我不需要做营销。所以只有你知道。答案是:这只是意味着操作系统之间存在差异,从而产生了根据每个平台进行不同处理的需求。编辑了问题以使其更清楚。
  • @hakre MarkR 在技术上是正确的。不能假设给定的网络堆栈比另一个更快。也就是说,我的经验是 PHP 有很多工具更适合 LAMP 环境(可能是因为 LAMP 或多或少被认为是 PHP 历史的一段美好时期)。
【解决方案2】:

你需要量化“整个反应”。

如果“整个响应”是指在套接字关闭之前写入套接字的所有数据,那么您需要使用循环获取更多数据。 recv 不保证在单个块中发送所有数据。

我强烈建议您不要使用古老的 socket_ 函数,而是使用 fsockopen 和 fread。

【讨论】:

    【解决方案3】:

    在 php 手册中说,如果类型设置为 PHP_NORMAL_READ,则读取以 \r \n 结尾 string socket_read ( 资源 $socket , int $length [, int $type = PHP_BINARY_READ ] )

    在您的情况下,可能必须是 socket_read($sock, 2048,PHP_NORMAL_READ) 以表明它必须在 \r\n

    否则,您可以尝试使用普通的 fsockopen,然后使用 fread 来代替......它通常在 linux 上运行良好。

    【讨论】:

    • 这是一个自定义服务器应答,它以二进制模式工作(以 \0 结尾)。所以这不是我的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多