详解Swoole TCP流数据边界问题解决方案_第1页
详解Swoole TCP流数据边界问题解决方案_第2页
详解Swoole TCP流数据边界问题解决方案_第3页
详解Swoole TCP流数据边界问题解决方案_第4页
详解Swoole TCP流数据边界问题解决方案_第5页
已阅读5页,还剩2页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第详解SwooleTCP流数据边界问题解决方案目录1.数据发送过程2.什么是数据边界2.1代码演示3.EOF解决方案3.1open_eof_check3.2open_eof_split3.3open_eof_check和open_eof_split差异4.固定包头+包体解决方案5.总结6.扩展知识6.1字节序

1.数据发送过程

首先由客户端将数据发往缓冲区(服务端并不是直接收到的),对于客户端来说,这次的数据即是发送成功了,对于服务端是否真正的收到他是不知道的,然后再由服务端从缓冲区中读取数据。图解:

2.什么是数据边界

因为TCP是流式传输,对于服务端来说并不知道此时在缓冲区内的数据是一次请求还是两次请求的,所以在服务端接收数据时需要根据指定字符或约定长度来对数据进行分包,这个分包的标志即是数据边界。否则可能会出现一次读取两条或多条数据,造成读取、解析数据出错。

2.1代码演示

可以用代码实现一下,假设客户端死循环往缓冲区不停输入“1”,即相当于每次的报文内容都是1,那么在服务端读取时收到的数据就是随机长度的。

客户端代码:

$client=newSwoole\Client(SWOOLE_SOCK_TCP);

if($client-connect('',9501,-1)){

while(true){

$client-send(1);

$client-close();

服务端代码:

$server=newSwoole\Server('',9501);

$server-on('connect',function($server,$fd){

echo"client:".$fd."connect";

$server-on('receive',function($server,$fd,$from_id,$data){

echo"receive:".$data.PHP_EOL;

$server-on('close',function($server){

});

运行结果

可以看到运行结果,服务端获取到的数据完全是随机的,有长有短,那么接下来我们说下如何解决这个问题。

3.EOF解决方案

第一种解决方案类似于我们http请求头的分隔符,在每次发送的数据包结尾处使用\r\n(可以配置)来结尾,当服务端从缓冲区中读取数据,根据指定字符来分割数据包,EOF有两种配置方案:

3.1open_eof_check

首先放出配置方式:

$server-set([

'open_eof_check'=true,

'package_eof'="\r\n"

]);

这种配置方式会对客户端发来的数据包进行检测,当发现结尾是\r\n时,才会投递给worker进程,也就是我们的onReceive回调,否则会一直拼接数据包,直到超出缓冲区或者超时才终止。但此方法有一个问题是可能会一次性收到多个数据包,因为他是从数据包的结尾处来进行检查的,在数据内容中存在\r\n时程序并不会发现,需要我们自己在应用代码中再次使用\r\n来拆分数据包。

客户端运行代码

$client=newSwoole\Client(SWOOLE_SOCK_TCP);

if($client-connect('',9501,-1)){

while(true){

$send2="HelloWorld\r\n";

$client-send($send2);

$client-close();

服务端代码

$server=newSwoole\Server('',9501);

$server-set([

'open_eof_check'=true,

'package_eof'="\r\n"

$server-on('connect',function($server,$fd){

echo"client:".$fd."connect";

$server-on('receive',function($server,$fd,$from_id,$data){

echo"receive:".$data;

$server-on('close',function($server){

$server-start();

运行结果

3.2open_eof_split

配置方式:

$server-set([

'open_eof_split'=true,

'package_eof'="\r\n"

]);

这种配置方式,服务端会对客户端发来的数据逐个字符进行检查,遇到\r\n就发送给worker进程,可以有效实现分包,但缺点是性能比较差。

运行结果:可以看到每次接收到一个HelloWorld(代码我就不贴了,只把服务端set配置改一下,其他都一样)

3.3open_eof_check和open_eof_split差异

open_eof_check只检查接收数据的末尾是否为EOF,因此它的性能最好,几乎没有消耗

open_eof_check无法解决多个数据包合并的问题,比如同时发送两条带有EOF的数据,底层可能会一次全部返回

open_eof_split会从左到右对数据进行逐字节对比,查找数据中的EOF进行分包,性能较差。但是每次只会返回一个数据包

4.固定包头+包体解决方案

引用一段官方文档的描述:

包长检测提供了固定包头+包体这种格式协议的解析。启用后,可以保证Worker进程onReceive每次都会收到一个完整的数据包。

长度检测协议,只需要计算一次长度,数据处理仅进行指针偏移,性能非常高,推荐使用。

可见官方是推荐使用这种方式的,就是配置比其他方案要复杂一些,首先贴一下配置:

$server-set([

//打开包长检测特性

'package_length_check'=true,

//包头中某个字段作为包长度的值,底层支持了10种长度类型。可参考pack()方法

'package_length_type'='N',

//length长度值在包头的第几个字节。

'package_length_offset'=8,

//从第几个字节开始计算长度,一般有2种情况:

//length的值包含了整个包(包头+包体),package_body_offset为0

//包头长度为N字节,length的值不包含包头,仅包含包体,package_body_offset设置为N

'package_body_offset'=16,

//设置最大数据包尺寸,单位为字节

'package_max_length'=81920

]);

下面是一个数据包结构例子,可以很好的体现了字段含义。

以上通信协议的设计中,包头长度为4个整型,16字节,length长度值在第3个整型处。因此package_length_offset设置为8,0-3字节为type,4-7字节为uid,8-11字节为length,12-15字节为serid。

下面来说一下代码实现:

客户端代码:

$client=newSwoole\Client(SWOOLE_SOCK_TCP);

$data="123456789012345678901234567890"

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论