swoole是用php快速开发高效的tcp/udp服务, 其中tcp是用的更多的一个场景,http虽然是基于tcp协议的,但和直接开发tcp服务仍是有明显的区别的。php
TCP是数据流前端
tcp是数据流,这是一个基本的概念,这里有两个要点:web
数据没有边界编程
你能够理解为水在一个水管里的流动,咱们不知道哪段数据是一个咱们须要的完整数据数组
收发有缓冲区服务器
好比:当水从一端流到了另外一端,咱们在收数据的时候,不可能每来一滴水就处理一次,这个缓冲区就至关于有了一个水桶,再接了必定的水以后内核再给数据交到用户空间,这样能够大大提高性能。swoole
那这里就有一个问题:因为这个特性,内核没法知道哪部分数据是你想要的,因此应用层拿到数据多是完整数据,也多是不完整或者一部分完整、一部分不完整(粘包),那么怎样能获得想要的数据呢?
网络
数据协议tcp
数据协议能够理解为一种约定,按照这个约定来处理收到的数据进而获得想要的数据,这个过程就称为拆包,那问题又来了?性能
web开发为何不用考虑粘包
由于web的http能够理解为一个公共的数据协议,在实现一个web服务器的时候,就必需按照这个协议来进行数据处理,这样保证在应用层获取到数据是完整的数据,http是典型的包头+包体的这个一个约定格式,有那现几个特色:
经过\r\n\r\n来区分包头和包体
包头经过\r\n来区分不一样的数据组
看个示例:
GET / HTTP/1.1
Host: www.swoole.com
Connection: keep-alive
这是一个典型的请求头,第一行约定了请求的方法(GET/POST/PUT/DELETE等)、请求地址、http协议版本, 从第二行开始,都是健:值的形式,反应到PHP里,就是$_SERVER超全局变里里 HTTP_开头的数据,因为这是GET请求,全部没有包体,若是是POST或者response响应,咱们能够看到包体,包体的长度经过包头里的Content-Length参数来控制的
Swoole怎么处理粘包
swoole考虑到在粘包是一个必需处理的过程,内置了两种方案:
约定结束符
这个相似于c里面的字符串数组,约定了\0表示字符串结束,在swoole里,能够的setting数组中,开启open_eof_check=true,并用package_eof来设置一个完整数据结尾字符。
举个例子:
"open_eof_check"=>true
"package_eof" = >'swooleend',
这里设置了package_eof = 'swooleend', 那client在每一个完整数据以后再拼上swooleend以后,再发送到swoole server,swoole server就能自动识别并拼出一个完整的包。这里又隐含了两个小问题?
a) 若是我一次收到了多个完整包怎么办?
那能够开启 open_eof_split=>true, 这样在onReceive里回调里拿到的数据就是一个完整的包了,不然须要业务层里自行explode('swooleend', $data)了,
b) 要保证业务数据里不能出现package_eof设置的字符,不然将致使数据错误了。
自定义包头包体
这种方式也很常见,特别是在二进制数据流中,原理是经过约定数据流的前几个字节来表示一个完整的数据有多长,从第一个数据到达以后,先经过读取固定的几个字节,解出数据包的长度,而后按这个长度继续取出后面的数据,依次循环。
相关配置:
'package_length_type' => 'N', //数据unpack方式,这里N表示无符号32位大端长整型,更多定义能够参考:http://php.net/manual/zh/function.pack.php
'package_length_offset'=> 0, //第N个字节是包长度的值
'package_body_offset' => 4, //第几个字节开始计算长度
'package_max_length' => 2000000, //协议最大长度
这个配置表示,前端4个字节是数据包长度,按照N的方式解析,最后一个配置表示数据包的最大长度,这个配置的目的是为了控制内存。
总结,粘包处理是不少新作服务器网络编程最容易碰到的问题,特别是长期作web开发的同窗,基本没有这个概念,因此经过这个思惟转换,更深刻的了解http协议,进而了解tcp协议,对服务器网络编程是很是有好处的。