一、前言
关于TCP网络传输粘包,网上很多人写了原理。总结起来就一句话:这里拿Server和Client长连接,Server和Client之间通过信令传输做说明:
Server发送的时候按照一条条信令发送,到达操作系统网络层,首先进入缓冲池,然后TCP协议层从池子中获取数据,传输给Client。我们知道TCP的传输有几个方案,比如,滑动窗口、1比特方案。所以Client收到的数据已经不可能是一个个完整的信令的。
个人理解TCP粘包的概念:它描述了一个场景:“信令是一个个紧挨着的,好像是被粘在一起了”。在把信令拆开之前我们要做一个必须的任务:规整数据。规整了socket数据,那么原始二进制信令就出来了。
二、分析
刚才我讲过,Client从Socket收到的数据是不确定的,可能是1Byte,可能100Byte。而即便相同的信令,内容不一样长度也是不同的。将信令一个个摘出来,需要一个关键的属性:信令长度。通常信令的两个字节是长度,其表明了该条信令的长度。Client需要一个Buffer,用于规整信令。
通常Client接收Socket数据,存在以下几种场景:
(图1)
上图2展示了:信令前两个字节(橙色标注)是200B,然而Client的Buffer只是接收到了100B,那么客户端什么也不做,等待后续的socket数据
(图2)
上图2展示了:信令前两个字节(橙色标注)是50B,然而Client的Buffer已经超过50B了,那么Client可以截取50B,当成完整的信令,给后续逻辑处理了。
(图3)
上图3跟,图2相似。
三、逻辑实现
需要注意的是:在获取前两个字节的时候,需要判断系统是大端,还是小端
/** * @brief 收到消息 * * @param data 数据指针 * @param length 数据长度 */ void onReceiveData(NSData * data) { if (data == NULL) { return; } [m_data appendData:data]; const Byte *packageData = (Byte *)[m_data bytes]; NSUInteger packageSize = [m_data length]; // parse packet unsigned short messageSize = 0; NSUInteger pos = 0; while (pos < packageSize) { if (pos + 2 < packageSize) { // can read message packet-size // read message packet-size messageSize = *((unsigned short *)(packageData + pos)); // 现为小头 //Byte tmp = (messageSize & 0xFF00) >> 8; //messageSize <<= 8; //messageSize |= tmp; if(messageSize <= packageSize - pos) { // there is a complate message if (m_callback) { m_callback->onDeliverMsg((packageData + pos), messageSize); } pos += messageSize; continue; } } break; } // deal with last bytes. if (pos < packageSize) { [m_data replaceBytesInRange:NSMakeRange(0, packageSize - pos) withBytes:(packageData + pos)]; [m_data setLength:packageSize - pos]; } else { [m_data setLength:0]; } }
(完)
时间: 2024-10-10 06:57:58