详解Swoole TCP流数据边界问题解决方案

admin3年前PHP教程30
1. 数据发送过程

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

2. 什么是数据边界

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

2.1 代码演示

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

客户端代码:

服务端代码:

运行结果

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

3.eof 解决方案

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

3.1 open_eof_check

首先放出配置方式:

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

客户端运行代码

服务端代码

运行结果

3.2 open_eof_split

配置方式:

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

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

3.3 open_eof_check 和 open_eof_split 差异

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

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

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

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

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

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

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

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

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

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

下面来说一下代码实现:

客户端代码:

服务端代码:

客户端运行结果

服务端运行结果

可以看到 客户端成功的把发送的数据回显, 服务端也打印出了接收到的所有数据, 其中有些字段在发送时是 16 进制的, 所以服务端在接收到之后需要进行进制转换, 我这里没有进行转换, 所以显示的数据是 10 进制的。

5. 总结

通过对比可以看出使用固定包头 + 包体的方式是效率最高的一种, 因为他是按照固定长度去读取的。期间专门去了解了 pack 函数的使用方法,但也不确定这么写到底对不对,如果有其他了解的仁兄可以慷慨解答一下,网上相关资料有点少,官方文档上也只给出了几个字段的释义。

6. 扩展知识

6.1 字节序

计算机硬件有两种储存数据的方式:大端字节序(big endian)和小端字节序(little endian)。

举例来说,数值 0x2211 使用两个字节储存:高位字节是 0x22,低位字节是 0x11。

大端字节序:高位字节在前,低位字节在后,这是人类读写数值的方法。小端字节序:低位字节在前,高位字节在后,即以 0x1122 形式储存。

这个前和后指的是内存地址,计算机处理字节时是不知道高低字节之分的,它只知道按顺序读取字节,先读第一个字节,再读第二个字节。

例如: 0x1234567 的读取顺序:

以上就是详解swoole tcp流数据边界问题解决方案的详细内容,更多关于swoole tcp流数据边界问题解决方案的资料请关注其它相关文章!

免责声明:本文内容来自用户上传并发布,站点仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。请核实广告和内容真实性,谨慎使用。

相关文章

PHP局部异常因子算法-Local Outlier Factor(LOF)算法的具体实现解析

这两天在完善自己系统的过程中要实现一个查找异常的功能,于是在朋友的指点下学习并实现了异常点查找的一个基本算法“局部异常因子算法-Local Outlier Factor(LOF)算法&rd...

服务器是否需要GPU租用江西显卡服务器怎么选择

显卡或GPU是专用服务器的重要组成部分。一块好的显卡将帮助您的服务器更有效地运行并处理更多任务。如果您希望为您的家庭或办公室设置专用服务器,请确保在您的计划中包含GPU。另一方面,使用带GPU的专用服...

江苏高防服务器如何安全有效防御DDOS攻击

江苏高防服务器如何安全有效防御DDOS攻击?DDoS(分布式拒绝服务攻击)是一种非常常见的网络攻击方式,它的目的是通过同时向一个网络资源发送大量的请求,以致于让该资源无法响应正常的请求。以下是几种江苏...

PHP内存溢出优化代码详解

相信很多人做大批量数据导出和数据导入的时候,经常会遇到PHP内存溢出的问题,在解决了问题之后,总结了一些经验,整理成文章记录下。优化点1、优化SQL语句,避免慢查询,合理的建立索引,查询指定的字段,s...

CPU和GPU服务器的区别在哪选择美国GPU服务器有什么优势

CPU和GPU服务器的区别在哪?要搞清楚CPU服务器和GPU服务器的区别,我们需要先了解清楚什么是CPU?什么是GPU?那么,感兴趣的朋友接下就跟随小编一起往下了解看看吧。CPU和GPU是什么?CPU...

php的配置文件是什么(php的框架有哪些)

php的配置文件是什么<强>php的装备文件是哪两个?php的装备文件一般是php。ini文件以及httpd.conf文件。php。ini文件必须命名为'p...