粘包产生的原因

网络通信方式主要有两种:TCPUDP。 UDP是基于报文传输的,发送几次Write(),接收端就会用几次Read(),每次读取一个报文,报文间不合并,多余缓冲区的报文会丢弃。TCP是基于数据流传输的,Write()Read()的次数不固定,报文间会以随机的方式合并,这就需要在接收时处理粘包了。通过上面的分析,我们可以发现,粘包只可能出现在流式传输中。 其粘包原因可能是下面两种情况:

  • 发送端需要等缓冲区才能发送数据,造成发送时就粘包;
  • 接收端未及时接收缓冲区的数据,多包一起接收,造成粘包;

粘包的解决方法

为了避免粘包,我们一般可以采取以下三种措施:

  • 发送端粘包,可以通过程序设置push指令,不等缓冲区满就立即发送数据(默认情况是等缓冲区满后再发送)。这种方法对于通信的传输效率会降低,有时也不是百分百能可靠。
  • 接收端粘包,可以通过优化程序、精简进程工作量、提高进程优先级等措施,使其及时接收数据。这种方法,你可以发现,有时候也是无法优化的,实现起来会比较难。
  • 采用自定义包头结构,人为控制多次合并,来避免粘包问题。这是常用的做法。

这里我针对项目中遇到的问题主要是接收数据时存在粘包问题,进行分析。TCP在接收数据时有四种情况:

  • 先接收到data1、再接收data2,这是我们所期待的;
  • 先接收到data1的部分数据,再接收到data1的余下部分以及data2的全部;
  • 先接收到data1的全部数据和data2的部分数据,再接收到data2的部分数据;
  • 一次性接收data1data2的全部数据。

对于后三种情况,我们都是要进行粘包处理的。

这里我将客户端接收小车数据的防粘包的处理方法代码示例出来,主要是定义了一个缓冲区,将从socket中读取的数据放入缓冲区,再从缓冲区中按固定结构去解析数据。

QByteArray m_buffer_car;
void SlotReadyReadDataCar()
{
    //socket缓冲区中没有数据直接返回
    if(socket->bytesAvailable() <=0)
        return;
    QByteArray buffer = socket->readAll();
    m_buffer_car.append(buffer);   //读取数据放入缓冲区
    qint64 tune_cmd;
    qint64 struct_count;
    qint64 total_bytes;
 
    int total_length = m_buffer_car.size();

    while(total_length)
    {
        QDataStream recv_data(m_buffer_car);    //从缓冲区中读取
        recv_data.setVersion(QDataStream::Qt_4_5);

        if(total_length < sizeof(qint64)*3)     //不够包头数据长度,等下次解析
            break;
        recv_data>>tune_cmd>>struct_count>>total_bytes;
        if(total_length < total_bytes + sizeof(qint64)*3)   //不够数据长度
            break;
        if(tune_cmd == _TCP_TUNE_CAR_)
        {
            for(int i = 0; i<struct_count; ++i)
            {
                recv_data>>st_car_monitor_info;
                emit updateCar(st_car_monitor_info, i);
                //这里可以对数据进一步处理
            }
        }
        //缓存多余的数据
        buffer = m_buffer.car.right(total_length - total_bytes - sizeof(qint64)*3)
       //更新长度
        total_length = buffer.size();
       //更新多余的数据
       m_buffer_car = buffer;
    }
}

通过上面的代码,我们可以看到,对于socket接收的数据包,关键在于合理地进行拆包,其拆包过程可以总结如下图:

TCP处理粘包_自定义