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

一、前言

        关于TCP网络传输粘包,网上很多人写了原理。总结起来就一句话(这里拿Server和Client长连接,Server和Client之间通过信令传输做说明)

Server发送的时候,虽然按照一条条信令发送,经过网络传输,到达客户端操作系统网络层,首先进入缓冲池,然后TCP协议从池子中获取数据,传输给Client App。但是,我们知道TCP的传输有多个方案,比如,滑动窗口、1比特方案。所以应用层 Client App收到的数据包,基本不可能是一个完整的Server发送的信令包了

        个人理解TCP粘包的概念,即它描述了一个场景:“信令是一个个紧挨着的,好像是被粘在一起了,不知道从哪儿将信令拆分”。在把信令拆开之前我们要做一个必须的任务:规整数据。规整了socket数据,那么原始信令就一条条规整出来了。

二、分析

        刚才已经讲过,Client从Socket收到的数据是不确定的,可能是1Byte,可能1000Byte。而即便相同的信令,内容不一样长度也是不同的。将信令一个个摘出来,需要一个关键的属性:信令长度。通常信令的前两个字节是长度(如下图中橙色块),其表明了该条信令的长度。Client需要一个Buffer,用于规整信令。

        通常Client接收Socket数据,存在以下几种场景:


(图1)

上图1展示了:信令前两个字节(橙色标注)是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];
    }
}

(完)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值