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

admin3年前PHP教程52
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流数据边界问题解决方案的资料请关注其它相关文章!

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

相关文章

高防IP有什么用?租用美国BGP高防物理机配置推荐?

什么是高防IP?IP就像服务器的门牌号,无论是访问还是管理服务器,都要通过IP进行。同理,如果黑客想要对目标服务器进行DDos攻击,也要知道这个目标服务器的IP,并用大量的无效流量数据对目标IP发起请...

微信小程序结合ThinkPHP5授权登陆后获取手机号

1.在我们授权登陆后会获取到用户的 sessionKey 这个是获取手机号不可分割的一部分上代码wxml:<button open-type="getPhoneNumber"...

php之app消息推送案例教程

如何用php实现app消息推送现在有很多的消息推送厂商,比如阿里云的消息推送,极光推送,融云的消息推送。他们的原理都是把sdk内置在app里面,达到消息推送的目的,通过一张图来了解一下,看不懂不要紧,...

江苏BGP高防服务器租用哪家好

江苏BGP高防服务器租用哪家好?选择租用江苏BGP高防服务器时,应该考虑以下因素:服务商信誉:选择知名度高、口碑好的服务商,可以保证服务质量和售后支持。配置要求:根据游戏服务器的实际需求,选择配置合适...

PHP中使用DOMDocument来处理HTML、XML文档的示例

目录解析HTML生成一个XML总结其实从PHP5开始,PHP就为我们提供了一个强大的解析和生成XML相关操作的类,也就是我们今天要讲的 DOMDocument 类。不过我估计大部分人在爬取网页时还是会...

php之php.ini配置文件讲解案例

[PHP] ; PHP还是一个不断发展的工具,其功能还在不断地删减 ; 而php.ini的设置更改可以反映出相当的变化, ; 在使用新的PHP版本前,研究一下php.in...