第九节 通讯代码精粹之收包解包实战
- 收包分析及包头结构定义
- 收包状态宏定义
- 收包实战代码
- 遗留问题处理
- 测试服务器收包避免推诿扯皮
一:收包分析及包头结构定义
- 发包:采用 包头+包体,其中包头中记录着整个包【包头+包体】的长度
- 包头:就是一个结构
- 一个包的长度不能超过 30000 个字节(应用程序方面,操作系统存在一定数量会拆包的行为不要理会),必须要有最大值。伪造恶意数据包,他规定这里300亿,我这个规定能够确保服务器程序不会处于非常危险的境地。
//头文件 ngx\_comm.h 中
#define \_PKG\_MAX\_LENGTH 30000
//每个包的最大长度【包头+包体】不超过这个数字,为了留出一些空间,实际上编码是,包头+包体长度必须不大于 这个值-1000【29000】
- 开始定义包头结构:COMM_PKG_HEADER
//包头结构
typedef struct \_COMM\_PKG\_HEADER
{
unsigned short pkgLen;
unsigned short msgCode;
int crc32;
}COMM\_PKG\_HEADER,\*LPCOMM\_PKG\_HEADER;
- 结构字节对齐问题,大家千万注意这个问题,不然会大错特 ,为了防止出现字节问题,所有在网络上传输的这种结构,必须都采用 1 字节对齐方式
#pragma pack(1)
// ......
#pragma pack()
二:收包状态宏定义
- 收包存在的问题:粘包,缺包(互联网恶劣环境导致)
- 收包思路:先手包头->根据包头中的内容确定包体长度并收包体,收包状态(状态机),定义几种收包的状态, 4种:0,1,2,3
//通信 收包状态定义
#define _PKG_HD_INIT 0 //初始状态,准备接收数据包头
#define _PKG_HD_RECVING 1 //接收包头中,包头不完整,继续接收中
#define _PKGBD\_INIT 2 //包头刚好收完,准备接收包体
#define \_PKG\_BD\_RECVING 3 //接收包体中,包体不完整,继续接收中,处理后直接回到\_PKG\_HD\_INIT状态
在 struct ngx_connection_s
结构体中需要添加一个结构体成员变量来表示此时 TCP 连接的状态
unsigned char curStat; //当前收包的状态
三:收包实战代码
聚焦在 ngx_wait_request_handler()
函数,同时设置好各种收包的状态
c->curStat = _PKG_HD_INIT;
c->precvbuf = c->dataHeadInfo;
c->irecvlen = sizeof(COMM_PKG_HEADER);
我们要求,客户端连入到服务器后,要主动地【客户端有义务】给服务器先发送数据包,服务器要主动收客户端的数据包,服务器按照 包头 + 包体 的格式来收包。
引入一个消息头【结构】STRUC_MSG_HEADER
,用来记录一些额外信息. 服务器收包时收到: 包头+包体, 再额外附加一个消息头, 消息头 + 包头 + 包体
再介绍一个分配和释放内存类 CMemory
本项目中不考虑内存池,内存池:对于提高程序运行效率帮助有效;但是 new 非常快; 内存池主要功能就是频繁的分配小块内存时,内存池可以节省额外内存开销【代价就是代码更复杂】;
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。