前言

TCP属于传输层的协议,传输层除了有TCP协议外还有UDP协议。那么UDP是否会发生粘包或拆包的现象呢?答案是不会。UDP是基于报文发送的,从UDP的帧结构可以看出,在UDP首部采用了16bit来指示UDP数据报文的长度,因此在应用层能很好的将不同的数据报文区分开,从而避免粘包和拆包的问题。而TCP是基于字节流的,虽然应用层和TCP传输层之间的数据交互是大小不等的数据块,但是TCP把这些数据块仅仅看成一连串无结构的字节流,没有边界;另外从TCP的帧结构也可以看出,在TCP的首部没有表示数据长度的字段,基于上面两点,在使用TCP传输数据时,才有粘包或者拆包现象发生的可能。

解决TCP粘包和拆包得方法

  • 发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。
  • 发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。
  • 可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开。

    等等。

封装一个带首部信息得包

一个网络数据包包括:首部大小(size_t),操作类型(int)+数据(char*)。

假设有定义的结构体CMD_TYPE_T是用来标识操作类型

typedef enum cmd_Type
{
Cmd_LoginRequest = 1,
Cmd_LoginResponse = 2,
} CMD_TYPE_T;

C语言

头文件tcpwrap.h

#ifndef TCPWRAP_H
#define TCPWRAP_H #include <memory>
#include <netinet/in.h>
#include <string.h> //指定字节对齐
#pragma pack(push,1) typedef struct
{
size_t length;
int cmd;
}Packet; #pragma pack(pop) class TcpDataWrap
{
public:
TcpDataWrap();
public:
void sendPackData(int sockfd, CMD_TYPE_T cmd, char* buf, int bufLen);
size_t recvUnpackData (int sockfd ,char*& recvBuf);
}; #endif

源文件tcpwrap.cpp

#include "tcpwrap.h"

TcpDataWrap::TcpDataWrap() {}

//对数据进行封包,然后发送
void TcpDataWrap::sendPackData(int sockfd, CMD_TYPE_T cmd, char* buf, int bufLen)
{
Packet pack;
size_t headSize = sizeof(pack.length);
size_t cmdSize = sizeof(pack.cmd);
size_t totalLen = headSize + cmdSize + bufLen; pack.length = totalLen - headSize;
pack.cmd = cmd;
char* sendBuf = new char[totalLen];
memset(sendBuf,0,totalLen);
memcpy(sendBuf, &pack.length, headSize);
memcpy(sendBuf + headSize, &pack.cmd, cmdSize);
memcpy(sendBuf + headSize + cmdSize, buf, bufLen); send(sockfd, sendBuf, totalLen,0); //发送数据
delete[] sendBuf;
} //收到得数据存储在recvbuf中,并返回recvBuf的大小
size_t TcpDataWrap::recvUnpackData (int sockfd ,char*& recvBuf)
{
size_t byteRead=0;
size_t headSize = 0;
while (byteRead <sizeof(size_t))
{
byteRead = recv(sockfd, &headSize, sizeof(size_t), MSG_PEEK); //查看数据长度是否满足包头的大小要求
}
recv(sockfd, &headSize, sizeof(size_t), 0); recvBuf = new char[headSize];
char* pData = recvBuf; size_t byteLeft = headSize; while (byteLeft>0)
{
byteRead = recv(sockfd, pData, byteLeft, 0);
byteLeft -= byteRead;
pData += byteRead;
}
return headSize;
}

Qt版

头文件tcpwrap.h

#ifndef TCPWRAP_H
#define TCPWRAP_H #include <memory>
#include <string.h>
#include <QTcpSocket> //字节对齐
#pragma pack(push,1) typedef struct
{
size_t length;
int cmd;
}Packet; #pragma pack(pop) class TcpDataWrap : public QObject
{
public:
TcpDataWrap();
public:
void sendPackData(QTcpSocket* sockfd, CMD_TYPE_T cmd, char* buf, size_t bufLen);
size_t recvUnpackData (QTcpSocket* sockfd, char*& buf);
}; #endif // TCPWRAP_H
#include "tcpwrap.h"
#include <QTcpSocket> TcpDataWrap::TcpDataWrap(){} //对数据进行封包,然后发送
void TcpDataWrap::sendPackData(QTcpSocket* sockfd, CMD_TYPE_T cmd, char* buf, size_t bufLen)
{
Packet pack;
size_t headSize = sizeof (pack.length); //头为8字节
size_t cmdSize = sizeof(pack.cmd); //操作位4字节
size_t totalLen = headSize + cmdSize + bufLen;
pack.length = totalLen - headSize;
pack.cmd = cmd; char* sendBuf = new char[totalLen]; //要发送的缓冲区
memset(sendBuf,0,totalLen);
memcpy(sendBuf,&pack.length,headSize);
memcpy(sendBuf+headSize,&pack.cmd,cmdSize);
memcpy(sendBuf+headSize+cmdSize, buf, bufLen);
sockfd->write(sendBuf,totalLen);
delete [] sendBuf;
} size_t TcpDataWrap::recvUnpackData (QTcpSocket* sockfd, char*& recvBuf)
{
size_t byteRead = 0;
size_t headSize = 0;
while (byteRead<sizeof (size_t))
{
byteRead=sockfd->bytesAvailable();
}
sockfd->read((char*)&headSize,sizeof (size_t));
recvBuf = new char[headSize];
char* pData = recvBuf;
size_t byteLeft = headSize;
while (byteLeft>0)
{
byteRead=sockfd->read(pData,byteLeft);
byteLeft -= byteRead;
pData += byteRead;
}
return headSize;
}

TCP的粘包和拆包问题及解决的更多相关文章

  1. TCP的粘包和拆包问题及解决办法(C#)

    本文参考:https://blog.csdn.net/wxy941011/article/details/80428470 原因 如果客户端连续不断的向服务端发送数据包时,服务端接收的数据会出现两个数 ...

  2. 关于TCP的粘包和拆包

    问题产生 一个完整的业务可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这个就是TCP的拆包和封包问题. 下面可以看一张图,是客户端向服务端发送包: 1. 第一种情况 ...

  3. tcp的粘包和拆包示例以及使用LengthFieldFrameDecoder来解决的方法

    粘包和拆包是什么? TCP协议是一种字节流协议,没有记录边界,我们在接收消息的时候,不能人为接收到的数据包就是一个整包消息 当客户端向服务器端发送多个消息数据的时候,TCP协议可能将多个消息数据合并成 ...

  4. Netty—TCP的粘包和拆包问题

    一.前言 虽然TCP协议是可靠性传输协议,但是对于TCP长连接而言,对于消息发送仍然可能会发生粘贴的情形.主要是因为TCP是一种二进制流的传输协议,它会根据TCP缓冲对包进行划分.有可能将一个大数据包 ...

  5. TCP的粘包、拆包及解决方法

    TCP粘包,拆包及解决方法 粘包拆包问题是处于网络比较底层的问题,在数据链路层.网络层以及传输层都有可能发生.我们日常的网络应用开发大都在传输层进行,由于UDP有消息保护边界,不会发生粘包拆包问题,因 ...

  6. netty 解决TCP粘包与拆包问题(一)

    1.什么是TCP粘包与拆包 首先TCP是一个"流"协议,犹如河中水一样连成一片,没有严格的分界线.当我们在发送数据的时候就会出现多发送与少发送问题,也就是TCP粘包与拆包.得不到我 ...

  7. 【Netty】TCP粘包和拆包

    一.前言 前面已经基本上讲解完了Netty的主要内容,现在来学习Netty中的一些可能存在的问题,如TCP粘包和拆包. 二.粘包和拆包 对于TCP协议而言,当底层发送消息和接受消息时,都需要考虑TCP ...

  8. C#网络编程学习(5)---Tcp连接中出现的粘包、拆包问题

    本文参考于CSDN博客wxy941011 1.疑问 我们使用第四个博客中的项目. 修改客户端为:连接成功后循环向服务器发送从1-100的数字.看看服务器会不会正常的接收100次数据. 可是我们发现服务 ...

  9. 【游戏开发】网络编程之浅谈TCP粘包、拆包问题及其解决方案

    引子 现如今手游开发中网络编程是必不可少的重要一环,如果使用的是TCP协议的话,那么不可避免的就会遇见TCP粘包和拆包的问题,马三觉得haifeiWu博主的 TCP 粘包问题浅析及其解决方案 这篇博客 ...

随机推荐

  1. JavaScript九九乘法表

    JavaScript九九乘法表 <script> for (var i = 1; i < 10; i++) { for (var j = 1; j <= i; j++) { d ...

  2. MySql 部分字段去重

    select * from personal_question_answer where answer_id in ( select min(answer_id) from personal_ques ...

  3. Html介绍,了解html代码的注释

    什么是代码注释?在代码编辑器中的注释代码是不会在浏览器窗口展示的!html代码注释的作用是帮助程序员标注代码的用途,过一段时间后再看你之前编写的代码,很快可以想起这个代码的用途.代码注释不仅方便程序员 ...

  4. 解决visual studio 2013编译过程中存在的无法打开kernel.lib问题

    1. 出现此类问题的原因 由于原visual studio文件中的安装中出现问题,所以原有的SDK(soft development kits)文件出现缺失: 2. 解决方法1 重新下载SDK工具,安 ...

  5. 安装MongoDB到Ubuntu(APT)

    运行环境 系统版本:Ubuntu 16.04.5 LTS 软件版本:mongodb-org-4.0.8 硬件要求:无 安装过程 1.配置APT-Mongodb存储库 ATP-Mongodb存储库由Mo ...

  6. Chapter1 递归与递推

    Chapter 1 递归与递推 时间复杂度(转载自yxc大佬) 一般ACM或者笔试题的时间限制是1秒或2秒. 在这种情况下,C++代码中的操作次数控制在 107107 为最佳. 下面给出在不同数据范围 ...

  7. sql 根据查询的记录生成序号的几种方式

    row_number()  order() 函数会为查询出来的每一行记录生成一个序号,依次排序且不会重复,注意使用row_number函数时必须要用over子句选择对某一列进行排序才能生成序号. ra ...

  8. mui下拉上拉(明一)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...

  9. gulp常用插件之gulp-rev-css-url使用

    更多gulp常用插件使用请访问:gulp常用插件汇总 gulp-rev-css-url这是一款用于在gulp-rev之后覆盖js.css文件中的URL进行替换. 更多使用文档请点击访问gulp-rev ...

  10. 让bat文件自动以管理员身份运行

    · 让bat文件自动以管理员身份运行 如何让bat文件自动以管理员身份运行,将这段写在bat文件的前头即可 : %1 mshta vbscript:CreateObject("Shell.A ...