关于粘包的问题

jorry

workerman当然是完整的没有粘包问题,但是我使用自己的程序接收workerman发送过来的信息时出现了这个问题,即从workerman发送的一条一条的信息变成了一整条的了,这个有一个通用的解决方案吗,我使用的是文本传递协议。。。求解

3980 5 0
5个回答

jorry

使用接收程序如下:

while ( $buf = stream_socket_recvfrom ( $new_conn, 65535 ) ) {
    $all_buffer .= $buf;
    if($buf === "\n") {
        break;
    }
    return $all_buffer;
}

接收一个一个字符接收是为了检查边界
结果很明显会有粘包的现象,因为一下子读那么多,而且接收的数据实际是上多条的,
在这里判断边界其实是没有意义的,那么怎么办呢:

while ( $buf = stream_socket_recvfrom ( $new_conn, 1 ) ) {
    $all_buffer .= $buf;
    if($buf == "\n") {
        break;
    }
    return $all_buffer;
}

我就一个一个的接收,这总行了吧,结果又是没什么:
没有了粘包问题,但接收的数据把我的整条信息拆开了,这又不是我想要的,为什么会在接收到 "\n"之前我的循环就跳出了呢?
这得考虑socket数据传输的特性了,这的规则是我们接收和发送定义的,而它自己的规则对我们来说没有实际的处理意义,即stream_socket_recvfrom返回来的可能为空。没能等到"\n"就已经跳出了。

最后walkor提供了以下方法:

$all_buffer = '';
while(1)
{
    $buf = stream_socket_recvfrom ( $new_conn, 1 );
    if(empty($buf))
    {
        if(feof($buf))
        {
            break;
        }
        continue;
    }
    $all_buffer .= $buf;
    if($buf == "\n")
    {
        break;
    }
}
return $all_buffer;

问题解决。

  • 暂无评论
walkor

处理方法类似,根据协议判断请求边界。
例如协议是 json+回车

客户端应该类似这样读

$client = stream_socket_client('tcp://ip:port');
$all_buffer = '';
while(1)
{
   // 每次读一个字节,因为每个字节都有可能是代表数据的结束的回车字符
   $buf = stream_socket_recvfrom($client, 1);
   // stream_socket_recvfrom 可能返回空,比如$client是非阻塞的,或者被信号打断、断开、超时等
   if(empty($buf) && $buf != '0')
   {
        // 链接断开就跳出
        if(feof($buf))
        {
           break;
        }
        // 继续
        continue;
   }
    // 拼接数据
    $all_buffer .= $buf;
    // 是请求边界就跳出
    if($buf == "\n")
    {
        break;
    }
}

当然上面的协议简单,但是由于是一个字节一个字节的读,对性能有一点点的损耗

如果对性能要求非常非常苛刻,可以采用 类似 包长+包体 的协议传输

  • 暂无评论
walkor

额,jorry已经回答了

  • 暂无评论
一路向北

看了一下代码。是不是

if(empty($buf))
{
// 链接断开就跳出
if(feof($buf))
{
break;
}
// 继续
continue;
}

这样就把字符中的空格给抹掉了?

  • 暂无评论
walkor

应该不会抹掉空格,但是会抹掉 '0' 字符,得加个判断

if(empty($buf) && $buf != '0')
{
// 链接断开就跳出
if(feof($buf))
{
break;
}
// 继续
continue;
}

  • 暂无评论
年代过于久远,无法发表回答
🔝