关于粘包的问题

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

jorry - 80

赞同来自:

使用接收程序如下:

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;
}

要回复问题请先登录注册