阅读 178

Swoole下TCP粘包问题的产生及解决办法

Swoole下TCP粘包问题的产生及解决办法

TCP在数据传输中,如果数据过大则会影响传输速度,则需要拆包。若数据过小,当我们连续而又快速的写入数据,数据会先保存到套接字缓冲区中,网卡会将多次写入的数据一并发送到服务器,此时就会发生粘包现象。即多个数据混合在一起发送。无法区分。

在这个过程中可能会出现3种情况:

  1 、正常:两个数据包逐一分开发送

  2 、粘包:两个包一同发送,

  3 、拆包:Server接收到不完整的或多出一部分的数据包

如下图展示(来自网络):

一:粘包现象展示

创建server.php

复制代码

<?php// swoole_tcp_server.php
//创建Server对象,监听 127.0.0.1:9501端口$serv = new Swoole\Server("127.0.0.1", 9501);$serv->set([//心跳检测,每三秒检测一次,10秒没活动就断开'heartbeat_idle_time'=>6,//连接最大的空闲时间'heartbeat_check_interval'=>3 //服务器定时检查]);//监听连接进入事件$serv->on('Connect', function ($serv, $fd) {    echo "Client ".$fd.": Connect.\n";
});//监听数据接收事件$serv->on('Receive', function ($serv, $fd, $from_id, $data) {    // 接收客户端的想你想
    echo "接收到".$fd."信息的".$data."\n";    $serv->send($fd, "Server: ");
});//监听连接关闭事件$serv->on('Close', function ($serv, $fd) {    echo "Client: ".$fd."Close.\n" ;
});echo "启动swoole tcp server 访问地址 127.0.0.1:9501 \n";//启动服务器$serv->start();

复制代码

创建client.php

复制代码

<?php/*
 * @Author: your name
 * @Date: 2021-03-24 10:56:50
 * @LastEditTime: 2021-04-20 15:13:24
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \www\swoole\tcp\client.php */$client = new swoole_client(SWOOLE_SOCK_TCP);//连接到服务器if (!$client->connect('127.0.0.1', 9501, 0.5))
{    die("connect failed.");
}//向服务器发送数据for ($i=0; $i < 10; $i++) {    sleep(1);//注意,这里我先暂停1秒发送
    $client->send($i);
}//从服务器接收数据$data = $client->recv();if (!$data)
{die("recv failed.");
}echo $data."\n";//关闭连接$client->close();?>

复制代码

第一次包含暂停:结果展示正确:

第二次此时去掉暂停:再次测试:

第二次出现的结果明显不是我期望打印的结果,此时发送的问题 就叫粘包。

 二:解决粘包问题

   1,EOF结束符协议

  即在每个数据包的结尾加上特殊的字符表示包已经结束。缺点:一定要确保该数据内不包含设定的字符。性能差。因为要遍历每个字节。

复制代码

// 服务端代码加入配置$serv->set([    'open_eof_split' => true,
    'package_eof' => "\r\n",]);// 客户端代码加入配置$client->set([    'open_eof_split' => true,
    'package_eof' => "\r\n",]);//客户端数据发送修改
//向服务器发送数据for ($i=0; $i < 10; $i++) {    $client->send($i."\r\n");
}

复制代码

  测试结果:

  1.1,换一种配置性能优于上面,但是只能解决分包问题,不能解决合包,具体设置如下

复制代码

// 服务端配置$serv-->set(array(    'open_eof_check' => true, //主要是这个参数变化
    'package_eof' => "\r\n",));// 客户端配置$client->set(array(    'open_eof_check' => true,
    'package_eof' => "\r\n",));

复制代码

  2,固定包头+包体协议(建议采用

  采用php的pack和unpack函数计算出传输的字符串长度,通过substr获取数据。

复制代码

// 服务端配置$serv->set([    // 'open_eof_check' => true,
    // 'package_eof' => "\r\n",
    'open_length_check' => true,
    'package_max_length' => 81920,
    'package_length_type' => 'n', //see php pack()
    'package_length_offset' => 0,
    'package_body_offset' => 2,]);// 客户端发送数据调整
//向服务器发送数据for ($i=0; $i < 10; $i++) {    $client->send(pack("n", strlen($i)) . $i);
}

复制代码

  测试结果:

  此处类似于:参考pack,unpack函数解释。

复制代码

// 发送数据$message = 'hello world';$sendMessage = pack("n", strlen($message)) . $message;$client->send($sendMessage);// 接受数据$dataLength = unpack("n", $data)[1];$message = substr($data, -$length);

复制代码


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐