1. 数据发送过程
我的官方群点击此处。
首先由客户端将数据发往缓冲区 (服务端并不是直接收到的), 对于客户端来说,这次的数据即是发送成功了, 对于服务端是否真正的收到他是不知道的, 然后再由服务端从缓冲区中读取数据。图解:
2. 什么是数据边界
因为 TCP 是流式传输,对于服务端来说并不知道此时在缓冲区内的数据是一次请求还是两次请求的,所以在服务端接收数据时需要根据指定字符或约定长度来对数据进行分包,这个分包的标志即是数据边界。否则可能会出现一次读取两条或多条数据,造成读取、解析数据出错。
2.1 代码演示
可以用代码实现一下,假设客户端死循环往缓冲区不停输入 “1”,即相当于每次的报文内容都是 1, 那么在服务端读取时收到的数据就是随机长度的。
客户端代码
$client = new Swoole\Client(SWOOLE_SOCK_TCP);
if ($client->connect('127.0.0.1', 9501, -1)) {
while(true) {
$client->send(1);
}
}
$client->close();
服务端代码
$server = new Swoole\Server('127.0.0.1', 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.1 open_eof_check
首先放出配置方式:
$server->set([
'open_eof_check' => true,
'package_eof' => "\r\n"
]);
这种配置方式会对客户端发来的数据包进行检测, 当发现结尾是 \r\n 时,才会投递给 worker 进程, 也就是我们的 onReceive 回调,否则会一直拼