前言

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. POST注入之sqlmap

    POST注入方法一加—form跑数据库sqlmap.py -u http://59.63.200.79:8815/Pass-05/index.php —form —dbs跑出数据库后查询表名 假设库名 ...

  2. Codeforces Round #340 (Div. 2) E XOR and Favorite Number 莫队板子

    #include<bits/stdc++.h> using namespace std; <<; struct node{ int l,r; int id; }q[N]; in ...

  3. C语言 运算符

    C语言 运算符 运算符优先级别 优先级 运算符 名称或含义 使用形式 结合方向 说明 1 [] 数组下标 数组名[常量表达式] 左到右 -- () 圆括号 (表达式)/函数名(形参表) -- . 成员 ...

  4. Mac IDEA 2019.3.1下载及破解,可激活至2089年

    背景 目前IDEA已更新到2019.3.3,但是下载这个版本后使用目前网上的常见破解方法会出现各种问题.比如使用注册码方式提示license key is in legacy format,或者使用L ...

  5. 使用IDA pro逆向ARM M系核心的Bin固件

    使用IDA pro逆向ARM M系核心的Bin固件 ​ 物联网和智能设备这两年还是比较火的,我们的手中或多或少都有了几个智能设备,比如手环,智能手表,或者门锁什么之类的东西,但是同学们在做逆向的时候, ...

  6. 请写一个java类,在任何时候都可以向它查询“你已经创建了多少个对象?”

    这个问题解决方法很简单,只要设置一个类的静态整型成员(事例中我设置的是n),初始化值为1,然后在其构造函数中添加语句使其+1(n++),这样需要查询创建了多少个对象时直接查询n的值就可以了,如下: p ...

  7. 获取指定key对应的node节点信息

    需求:之前写的脚本(https://www.cnblogs.com/imdba/p/10197192.html),每个node上都只有一个slot段范围的情况,本次通过测试,实现了,任意段范围的获取方 ...

  8. 下载thinkphp5.0

    在服务器文件中右击Git_Bash_Here打开 在命令框写入 首先克隆下载应用项目仓库   git clone https://github.com/top-think/think tp5 tp5代 ...

  9. luogu P1736 创意吃鱼法

    #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #i ...

  10. 深入理解Java内存模型中的虚拟机栈

    深入理解Java内存模型中的虚拟机栈 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域都会有各自的用途,以及创建和销毁的时间,有的区域会随着虚拟机进程的启 ...