TCP的粘包和拆包问题及解决
前言
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的粘包和拆包问题及解决的更多相关文章
- TCP的粘包和拆包问题及解决办法(C#)
本文参考:https://blog.csdn.net/wxy941011/article/details/80428470 原因 如果客户端连续不断的向服务端发送数据包时,服务端接收的数据会出现两个数 ...
- 关于TCP的粘包和拆包
问题产生 一个完整的业务可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这个就是TCP的拆包和封包问题. 下面可以看一张图,是客户端向服务端发送包: 1. 第一种情况 ...
- tcp的粘包和拆包示例以及使用LengthFieldFrameDecoder来解决的方法
粘包和拆包是什么? TCP协议是一种字节流协议,没有记录边界,我们在接收消息的时候,不能人为接收到的数据包就是一个整包消息 当客户端向服务器端发送多个消息数据的时候,TCP协议可能将多个消息数据合并成 ...
- Netty—TCP的粘包和拆包问题
一.前言 虽然TCP协议是可靠性传输协议,但是对于TCP长连接而言,对于消息发送仍然可能会发生粘贴的情形.主要是因为TCP是一种二进制流的传输协议,它会根据TCP缓冲对包进行划分.有可能将一个大数据包 ...
- TCP的粘包、拆包及解决方法
TCP粘包,拆包及解决方法 粘包拆包问题是处于网络比较底层的问题,在数据链路层.网络层以及传输层都有可能发生.我们日常的网络应用开发大都在传输层进行,由于UDP有消息保护边界,不会发生粘包拆包问题,因 ...
- netty 解决TCP粘包与拆包问题(一)
1.什么是TCP粘包与拆包 首先TCP是一个"流"协议,犹如河中水一样连成一片,没有严格的分界线.当我们在发送数据的时候就会出现多发送与少发送问题,也就是TCP粘包与拆包.得不到我 ...
- 【Netty】TCP粘包和拆包
一.前言 前面已经基本上讲解完了Netty的主要内容,现在来学习Netty中的一些可能存在的问题,如TCP粘包和拆包. 二.粘包和拆包 对于TCP协议而言,当底层发送消息和接受消息时,都需要考虑TCP ...
- C#网络编程学习(5)---Tcp连接中出现的粘包、拆包问题
本文参考于CSDN博客wxy941011 1.疑问 我们使用第四个博客中的项目. 修改客户端为:连接成功后循环向服务器发送从1-100的数字.看看服务器会不会正常的接收100次数据. 可是我们发现服务 ...
- 【游戏开发】网络编程之浅谈TCP粘包、拆包问题及其解决方案
引子 现如今手游开发中网络编程是必不可少的重要一环,如果使用的是TCP协议的话,那么不可避免的就会遇见TCP粘包和拆包的问题,马三觉得haifeiWu博主的 TCP 粘包问题浅析及其解决方案 这篇博客 ...
随机推荐
- Linux服务器被入侵后的处理过程(转发阿良)
Linux服务器被入侵后的处理过程 突然,频繁收到一组服务器 ping 监控不可达邮件,赶紧登陆 zabbix 监控系统查看流量状况. 可见流量已经达到了 800M 左右,这肯定不正常了,马上尝试 ...
- 今天带来compass的使用方式
一.为什么我们要使用compass呢 Experience cleaner markup without presentational classes. It’s chock full of the ...
- P5788 【模板】单调栈
---------------- 链接:Miku ---------------- 单调栈模板终于不是一堆蓝题了!!!!!!!!!!!! 单调栈,就是单调的栈,栈内元素都是单调的. 题目要求我们求出来 ...
- 五种编程语言解释数据结构与算法——顺序表3(JavaScript与Python语言实现)
7.JavaScript语言实现 7.1.用ES6语法编写顺序表类 //1.创建类 class MyList { //1. initList(&L):初始化表.构造一个空的线性表.放回值应该是 ...
- MySQL和MariaDB安全初始化
通过yum安装mysql(5.x)后往往需要进行一些安全类的初始化设置: 安装完数据库后执行mysql_secure_installation命令,会出现安全相关的交互界面. 按提示操作.
- MySQL常用命令.md
链接数据库 ## -h/-u这些后面没有空格 ./bin/mysql -h[ip地址] -P[端口号] -u[用户名] -p[密码] 查看建表语句 show create table [table_n ...
- 自定义配置JNOJ
OJ 全名 online judge 在线判题系统,对于从事编程竞赛的人来说一点都不陌生,今天我们讨论的是怎么样自定义搭建 推荐的开源的OJ有hustOJ,JNOJ 因为hustOJ 是一键安装脚本, ...
- 【47】迁移学习(Transfer Learning)
迁移学习(Transfer Learning) 如果你要做一个计算机视觉的应用,相比于从头训练权重,或者说从随机初始化权重开始,如果你下载别人已经训练好网络结构的权重,你通常能够进展的相当快,用这个作 ...
- Centos 7 下安装 samba 服务
yum install samba 配置文件在:/etc/samba/smb.conf [global] #添加下面这句 map to guest = Bad User #这个选项是保证匿名访问! # ...
- scrapy(一)--Pycharm创建scrapy项目
1.环境 操作系统:windows10. python版本:python3.6,Anaconda(将Anaconda3\Scripts;路径添加到环境变量Path中) pycharm:pycharm2 ...