前言

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. 【转】JS 的 new 到底是干什么的?

    原文:https://zhuanlan.zhihu.com/p/23987456?refer=study-fe 大部分讲 new 的文章会从面向对象的思路讲起,但是我始终认为,在解释一个事物的时候,不 ...

  2. Angular4.0环境搭建

    1.安装nodejs 先装nodejs,如果你的电脑已经装过了,最好确认是比较新的版本,否则可能会出问题 下载地址:https://nodejs.org/en/download/ 安装过程很简单,一直 ...

  3. node的httpserver简单创建

    1.设计原则为文件夹名字可以依据资源来命名,静态资源统一命名 ps:路径中绝对和相对路径,依据server.js本身的位置而言 const http = require("http" ...

  4. dir()和vars()的区别就是

    ------------恢复内容开始------------ dir()只打印属性(属性,属性......) 而vars()则打印属性与属性的值(属性:属性值......) >> a='a ...

  5. Asp.net core 3.1+EF Core2.2.6+Oracle.EntityFrameworkCore2.1.19连接Oracle数据库

    Asp.net Core 3.1+EF Core2.2.6+Oracle.EntityFrameworkCore2.1.19连接Oracle数据库 1.前言 本次主要采用Asp.net core3.1 ...

  6. Python爬虫连载9-JS加密之“盐”​、ajax请求

    一.JS加密之“盐”​ 1.salt属性“盐":多用于密码学,比如我们的银行卡是六位密码,但是实际上在银行的系统里,我们输入密码后,会给原始的密码添加若干字符,形成更加难以破解的密码.这个过 ...

  7. 9个常用的正则表达式-sunziren

    正数字:/^[1-9]{1}[0-9]*$|^0{1}\.{1}[0-9]+$|^[1-9]{1}[0-9]*\.{1}[0-9]+$/ 用户名:/^[a-z0-9_-]{3,16}$/ 密码:/^[ ...

  8. STL-set&&multiset 集合

    #include <iostream> #include <cstdio> #include <set> // 仿函数的原型 //struct greaters / ...

  9. CentOS8中进行IP和主机名的网络配置的过程图解

    摘要: 很多人不知道如何在字符界面下配置主机名和ip,所以写了这个文章,本人也是新手,希望指出错误与不足.(本文只是在字符界面下教程) 一.输入你的账号密码登录 1)ifconfig 查看你目前的 主 ...

  10. 安装MySQL被提示缺少msvcr120.dll,msvcp120.dll或提示0xc00007b错误

    踩坑经历: 我的电脑昨天重装了win10系统,在安装MySQL时被提示缺少msvcr120.dll,我去下载msvcr120.dll后放入System32中,又被提示缺少msvcp120.dll,然后 ...