第九节 通讯代码精粹之收包解包实战

  1. 收包分析及包头结构定义
  2. 收包状态宏定义
  3. 收包实战代码
  4. 遗留问题处理
  5. 测试服务器收包避免推诿扯皮

一:收包分析及包头结构定义

  • 发包:采用 包头+包体,其中包头中记录着整个包【包头+包体】的长度
  • 包头:就是一个结构
  1. 一个包的长度不能超过 30000 个字节(应用程序方面,操作系统存在一定数量会拆包的行为不要理会),必须要有最大值。伪造恶意数据包,他规定这里300亿,我这个规定能够确保服务器程序不会处于非常危险的境地。
//头文件 ngx\_comm.h 中  
#define \_PKG\_MAX\_LENGTH     30000  
//每个包的最大长度【包头+包体】不超过这个数字,为了留出一些空间,实际上编码是,包头+包体长度必须不大于 这个值-1000【29000】
  1. 开始定义包头结构:COMM_PKG_HEADER
//包头结构  
typedef struct \_COMM\_PKG\_HEADER  
{  
 unsigned short pkgLen;    
 unsigned short msgCode;   
 int            crc32;      
}COMM\_PKG\_HEADER,\*LPCOMM\_PKG\_HEADER;  
  1. 结构字节对齐问题,大家千万注意这个问题,不然会大错特 ,为了防止出现字节问题,所有在网络上传输的这种结构,必须都采用 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 非常快; 内存池主要功能就是频繁的分配小块内存时,内存池可以节省额外内存开销【代价就是代码更复杂】;


查子木
4 声望1 粉丝

学习 交流 一起进步