首页 > 代码库 > TCP网络传输“粘包”问题,经典解决(附代码)

TCP网络传输“粘包”问题,经典解决(附代码)

一、前言

        关于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];
    }
}

(完)

TCP网络传输“粘包”问题,经典解决(附代码)