tcp通信,解决断包、粘包的问题

1、TCP和UDP的区别

  • TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的(数据没有界限)
  • UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。


这样就会造成如下三种情况:

1.接收端依次从发送端接收Msg1,Msg2;(正常)

2.Msg1被发送端分割为多段发送给接收端(或者接收端先接收到Msg1和Msg2的部分数据,再接收到msg2剩下的数据);(断/拆包)

3.Msg1和Msg2粘和在一起被发送端一次发送给接收端;(粘包)

PS:因此,通常我们需要将接收的消息通过整理,然后分别合成一个我们需要的数据包,然后才将这个消息发送给接收端。

2、解决TCP断包、粘包的的代码

1. 协议如下

报头

长度

数据段(N个字节)

校验和

0x55AA

len

命令类型(id)

目标地址

主机地址

预留

其它

CRC

2 个字节

2 个字节(从报头到检验和所有长度)

n1个字节

n2个字节

n3个字节

n3个字节

n4个字节

1个字节(从报头到数据完的校验和)。

 2.代码如下:

void TcpClient::rcvSlot()
{
  static QByteArray allBa;  //用来存所有文件
  allBa.append(tcpClient->readAll());  //读取数据
  int head = allBa.indexOf(HEAD);    //报文头的位置,HEAD=0x55aa
  uint16_t length = 0;                               //报文(协议中)的长度
  QByteArray currentBa;
  bool checkResult = false;
  while (-1 != head)           //报文头检验
  {
    allBa = allBa.mid(head);      //去掉报头之前的
    memcpy(&length, allBa.data() + 2, 2);
    if (allBa.size() >= length)      //长度够,进行解析。报文长度校验
    {
      currentBa = allBa.mid(0, length); //获取当前完整包
      id = Mymethod:isCRC(currentBa);  //报文的和校验和id校验。(校验通过返回大于0,异常返回-1)
      if ( id)    //通过校验将数据发送
      {
        emit getPacketSignal(id, currentBa);
        qDebug() << "接收指令:" << currentBa.toHex().toUpper();

        allBa.remove(0,length+position);    //通过校验去掉取出的内容和报文头之前的内容
      }

      else

        allBa.remove(0,position+2);    //未通过校验则去掉报文头

    }

    else

      break;

    head = allBa.indexOf(HEAD);   //刷新报文头的位置
  }
}

PS:本人第一次写,若有不足之处请大家多多指出,希望能够和大家一起持续进步!

 

posted @ 2018-08-17 11:32  xianyongchao  阅读(648)  评论(0编辑  收藏  举报