# TCP 粘包处理

**TCP通信特点**

* 1. TCP 是流式协议没有消息边界，客户端向服务器端发送一次数据，可能会被服务器端分成多次收到。客户端向服务器端发送多少数据。服务器端可能一次全部收到。
* 2.保证传输的可靠性，顺序。
* 3.TCP拥有拥塞控制，所以数据包可能会延后发送。

**没有消息边界:**

可以理解为水在一个水管里的流动，我们不知道哪段数据是一个我们需要的完整数据

**收发有缓冲区:**

比如：当水从一端流到了另一端，我们在收数据的时候，不可能每来一滴水就处理一次，这个缓冲区就相当于有了一个水桶，再接了一定的水之后内核再给数据交到用户空间，这样可以大大提升性能。

## 什么是 TCP 粘包？

TCP 粘包是指发送方发送的若干包数据 到 接收方接收时粘成一包，从接收缓冲区看，后一包数据的头紧接着前一包数据的尾。

**TCP 出现粘包的原因？**

发送方：发送方需要等缓冲区满才发送出去，造成粘包

接收方：接收方不及时接收缓冲区的包，造成多个包接收

参考资料:[TCP粘包问题分析和解决（全）](https://www.cnblogs.com/kex1n/p/6502002.html)

## Swoole怎么处理粘包

### 方式1: **EOF 结束协议**

通过约定结束符，来确定包数据是否发送完毕

开启`open_eof_check=true`,并用`package_eof`来设置一个完整数据结尾字符，同时设置自动拆分`open_eof_split`

示例：

```php
$serv->set([
    'worker_num' => 2, //设置进程
    'open_eof_check' => true, // 打开EOF检测
    'package_eof' => "\r\n", // 设置EOF
    'open_eof_split' => true // 开启自动拆分
]);
```

注意：

* 1、 要保证业务数据里不能出现package\_eof设置的字符,否则将导致数据错误了。
* 2、可以手动拆包，去掉`open_eof_split`,自行 `explode("\r\n", $data)`,然后循环发送

### 方式2: **固定包头+包体协议**

这种方式也非常常见，原理是通过约定数据流的前几个字节来表示一个完整的数据有多长，从第一个数据到达之后，先通过读取固定的几个字节，解出数据包的长度，然后按这个长度继续取出后面的数据，依次循环。

配置示例:

```php
$client->set([
    'open_length_check' => true,//打开包长检测特性
    'package_max_length' => 1024*1024,//最大允许的包长度
    'package_length_type' => 'N', //长度字段的类型,对应pack()方法的format字段
    'package_length_offset' => 0,//从第几个字节开始是长度，比如包头长度为120字节，第10个字节为长度值，这里填入9
    'package_body_offset' => 4,//从第几个字节开始计算长度
]);
```

* open\_length\_check：打开包长检测特性
* package\_length\_type：长度字段的类型，固定包头中用一个4字节或2字节表示包体长度。
* package\_length\_offset：从第几个字节开始是长度，比如包头长度为120字节，第10个字节为长度值，这里填入9（从0开始计数）
* package\_body\_offset：从第几个字节开始计算长度，比如包头为长度为120字节，第10个字节为长度值，包体长度为1000。如果长度包含包头，这里填入0，如果不包含包头，这里填入120
* package\_max\_length：最大允许的包长度。因为在一个请求包完整接收前，需要将所有数据保存在内存中，所以需要做保护。避免内存占用过大。

**package\_length\_type 长度值的类型**

长度值的类型，接受一个字符参数，与php的pack函数一致。目前swoole支持10种类型：

* c：有符号、1字节
* C：无符号、1字节
* s：有符号、主机字节序、2字节
* S：无符号、主机字节序、2字节
* n：无符号、网络字节序、2字节 (常用)
* N：无符号、网络字节序、4字节 (常用)
* l：有符号、主机字节序、4字节（小写L）
* L：无符号、主机字节序、4字节（大写L）
* v：无符号、小端字节序、2字节
* V：无符号、小端字节序、4字节

## 资料

[swoole基础-swoole之粘包问题](https://www.cnblogs.com/JsonM/articles/9283037.html)

[swoole 粘包/网络通信协议设计](https://blog.csdn.net/z457181562/article/details/82805808)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xiaoxiami.gitbook.io/swoole/gai-nian/tcp-nian-bao-chu-li.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
