Openssl数据安全传输平台006:粘包的处理-代码框架及实现-TcpSocket.cpp

文章目录

  • 0. 代码仓库
  • 1. TCP通信粘包问题
  • 2. 粘包、拆包表现形式
    • 2.1 正常情况
    • 2.2 两个包合并成一个包
    • 2.3 出现了拆包
  • 3. 粘包的处理-参考仓库中的文件TcpSocket.cpp
    • 3.1 发送数据时候的处理
    • 3.2 接收数据时候的处理

0. 代码仓库

https://github.com/Chufeng-Jiang/OpenSSL_Secure_Data_Transmission_Platform

1. TCP通信粘包问题

tcp是以流动的方式传输数据,没有边界的一段数据。像打开自来水管一样,连成一片,没有边界。传输的最小单位为一个报 文段(segment)。

tcp Header中有个Options标识位,常见的标识为mss(Maximum Segment Size)指的是:连接层每次传输的数据有个最大限制MTU(Maximum Transmission Unit),一般是1500比特,超过这个量要分成多个报文段,mss则是这个最大限制减去TCP的header,光是要传输的数据的大小,一般为1460比特。换算成字节, 也就是180多字节。

tcp为提高性能,发送端会将需要发送的数据发送到缓冲区,等待缓冲区满了之后,再将缓冲中的数据发送到接收方。 同理,接收方也有缓冲区这样的机制,来接收数据。

发现,如果客户端连续不断的向服务端发送数据包时,服务端接收的数据会出现两个数据包粘在一起的情况,这就是TCP协议中经常会遇到的粘包以及拆包的问题。

2. 粘包、拆包表现形式

现在假设客户端向服务端连续发送了两个数据包,用packet1和packet2来表示,那么服务端收到的数据可以分为三种,现列举如下:

2.1 正常情况


第一种情况,接收端正常收到两个数据包,即没有发生拆包和粘包的现象,此种情况不在本文的讨论范围内。

2.2 两个包合并成一个包

第二种情况,接收端只收到一个数据包,由于TCP是不会出现丢包的,所以这一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。这种情况由于接收端不知道这两个数据包的界限,所以对于接收端来说很难处理。
在这里插入图片描述

2.3 出现了拆包

第三种情况,这种情况有两种表现形式,如下图。接收端收到了两个数据包,但是这两个数据包要么是不完整的,要么就是多出来一块,这种情况即发生了拆包和粘包。在这里插入图片描述

3. 粘包的处理-参考仓库中的文件TcpSocket.cpp

3.1 发送数据时候的处理

添加4个字节的数据头,存储数据块的长度。

dataLen为发送原始数据的长度,在此基础上添加4个字节的长度,并开辟netdata空间用来存储数据。

int dataLen = sendData.size() + 4;
unsigned char *netdata = (unsigned char *)malloc(dataLen);

在发送的时候,需要从主机字节序转换为网络字节序。

  • 先求将原始数据转换成网络字节序的长度大小
    int netlen = htonl(sendData.size());

  • 再将原始数据的长度,拷贝到开辟的空间netdata前4个位置
    memcpy(netdata, &netlen, 4);

  • 最后将原始数据内容拷贝到开辟的空间netdata中第4个字节以后的位置
    memcpy(netdata + 4, sendData.data(), sendData.size());

int TcpSocket::sendMsg(string sendData, int timeout)
{// 返回0->没超时, 返回-1->超时int ret = writeTimeout(timeout);if (ret == 0){int writed = 0;int dataLen = sendData.size() + 4;// 添加的4字节作为数据头, 存储数据块长度unsigned char *netdata = (unsigned char *)malloc(dataLen);if (netdata == NULL){ret = MallocError;printf("func sckClient_send() mlloc Err:%d\n ", ret);return ret;}// 转换为网络字节序int netlen = htonl(sendData.size());memcpy(netdata, &netlen, 4);memcpy(netdata + 4, sendData.data(), sendData.size());// 没问题返回发送的实际字节数, 应该 == 第二个参数: dataLen// 失败返回: -1writed = writen(netdata, dataLen);......

3.2 接收数据时候的处理

  • 先读包头的4个字节并转换成主机字节序,就知道报文有多长。
    readn函数用于读取网络字节流的文件到缓存netdatalen空间中
    ret = readn(&netdatalen, 4); //读包头 4个字节
    int n = ntohl(netdatalen);

  • 根据包头中记录的数据大小申请内存, 接收数据,添加一个‘\0’结束符
    char* tmpBuf = (char *)malloc(n + 1);

  • 根据长度读数据
    ret = readn(tmpBuf, n);

string TcpSocket::recvMsg(int timeout)
{// 返回0 -> 没超时就接收到了数据, -1, 超时或有异常int ret = readTimeout(timeout); if (ret != 0){if (ret == -1 || errno == ETIMEDOUT){printf("readTimeout(timeout) err: TimeoutError \n");return string();}else{printf("readTimeout(timeout) err: %d \n", ret);return string();}}int netdatalen = 0;ret = readn(&netdatalen, 4); //读包头 4个字节if (ret == -1){printf("func readn() err:%d \n", ret);return string();}else if (ret < 4){printf("func readn() err peer closed:%d \n", ret);return string();}int n = ntohl(netdatalen);// 根据包头中记录的数据大小申请内存, 接收数据char* tmpBuf = (char *)malloc(n + 1);if (tmpBuf == NULL){ret = MallocError;printf("malloc() err \n");return NULL;}ret = readn(tmpBuf, n); //根据长度读数据if (ret == -1){printf("func readn() err:%d \n", ret);return string();}else if (ret < n){printf("func readn() err peer closed:%d \n", ret);return string();}tmpBuf[n] = '\0'; //多分配一个字节内容,兼容可见字符串 字符串的真实长度仍然为nstring data = string(tmpBuf);// 释放内存free(tmpBuf);return data;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/114925.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Node学习笔记之Express框架

一、express 介绍 express 是一个基于 Node.js 平台的极简、灵活的 WEB 应用开发框架&#xff0c;官方网址&#xff1a;https://www.expressjs. com.cn/ 简单来说&#xff0c;express 是一个封装好的工具包&#xff0c;封装了很多功能&#xff0c;便于我们开发 WEB 应用&…

局域网下多台windows电脑时间同步

windows时间同步 最近在项目中遇见了多台windows电脑的时间同步问题。在这个项目中&#xff0c;有五台电脑&#xff0c;五台电脑处于同一局域网下&#xff0c;其中有一台可以连接互联网&#xff08;A电脑&#xff09;。我需要将其他四台电脑&#xff08;B、C、D、E电脑&#xf…

fail-fast 和 fail-safe 迭代器

fail-fast 和 fail-safe 迭代器是两种不同的迭代器设计策略&#xff0c;用于在遍历集合&#xff08;如 ArrayList、HashMap&#xff09;时处理并发修改的情况。它们的行为和应对策略有所不同&#xff1a; Fail-Fast 迭代器&#xff1a; Fail-Fast 迭代器在遍历集合期间&#x…

连接Mumu模拟器使用ADB

要连接Mumu模拟器使用ADB&#xff0c;您可以按照以下详细步骤进行操作&#xff1a; 安装ADB驱动程序&#xff1a;在您的计算机上安装ADB驱动程序。ADB是Android Debug Bridge的缩写&#xff0c;它允许您与Android设备进行通信。您可以从Android开发者网站&#xff08;https://d…

图论03-【无权无向】-图的深度优先DFS遍历-路径问题/检测环/二分图

文章目录 1. 代码仓库2. 单源路径2.1 思路2.2 主要代码 3. 所有点对路径3.1 思路3.2 主要代码 4. 路径问题的优化-提前结束递归4.1 思路4.2 主要代码 5. 检测环5.1 思路5.2 主要代码 6. 二分图6.1 思路6.2 主要代码6.2.1 遍历每个联通分量6.2.2 递归判断相邻两点的颜色是否一致…

Jenkins 相关内容

Jenkins 相关内容 什么是 Jenkins&#xff0c;它是如何工作的&#xff1f;Jenkins 中自由式项目和管道之间的区别什么是Jenkins管道&#xff0c;它们如何工作&#xff1f;第一次如何安装Jenkins并进行设置&#xff1f;什么是 Jenkins 插件&#xff0c;如何安装它们&#xff1f;…

julia 笔记:复合类型 struct

1 struct关键字 1.1 声明 struct关键字与复合类型一起引入&#xff0c;后跟一个字段名称的块&#xff0c;可选择使用 :: 运算符注释类型 struct Foobarbaz::Intqux::Float64end 没有类型注释的字段默认为 Any 类型&#xff0c;所以可以包含任何类型的值。 1.2 创建新对象 …

IO/NIO交互模拟及渐进式实现

IO IO Server public class SocketServer {public static void main(String[] args) {//server编号和client编号对应&#xff0c;优缺点注释在server端//server1();//server2();server3();}/*** server1的缺点&#xff1a;* 1、accept()方法阻塞了线程&#xff0c;要等客户端…

2023年09月青少年软件编程C语言二级真题答案——持续更新.....

数组指定部分逆序重放 将一个数组中的前k项按逆序重新存放。例如,将数组8,6,5,4,1前3项逆序重放得到5,6,8,4,1。 时间限制:1000 内存限制:65536 输入 输入为两行: 第一行两个整数,以空格分隔,分别为数组元素的个数n(1 < n < 100)以及指定的k(1 <= k <= n)。…

【Qt之JSON文件】QJsonDocument、QJsonObject、QJsonArray等类介绍及使用

Qt之JSON相关类介绍 QJsonDocument常用函数枚举类型 QJsonDocument::DataValidation枚举类型 QJsonDocument::JsonFormat构造函数静态函数成员函数示例 QJsonObject常用函数构造函数&#xff1a;成员函数&#xff1a; QJsonObject 与 QVariantMap 相互转换 QJsonArray常用函数构…

无论有没有按钮,iPhone都可以进行截屏操作!如何在iPhone上截屏

通过简单的按键组合&#xff0c;可以很容易地将iPhone屏幕的图片捕获到图像文件中&#xff0c;并保存到照片库中。以下是操作方法。 什么是屏幕截图 屏幕截图是指通常包含你在设备屏幕上看到的内容的精确副本的图像。在设备内拍摄的数字屏幕截图通常使用相机拍摄物理屏幕的照…

AD9371 官方例程HDL详解之JESD204B TX_CLK生成 (二)

AD9371 系列快速入口 AD9371ZCU102 移植到 ZCU106 &#xff1a; AD9371 官方例程构建及单音信号收发 ad9371_tx_jesd -->util_ad9371_xcvr接口映射&#xff1a; AD9371 官方例程之 tx_jesd 与 xcvr接口映射 AD9371 官方例程 时钟间的关系与生成 &#xff1a; AD9371 官方…

HTTPS、SSL/TLS,HTTPS运行过程,RSA加密算法,AES加密算法

1、为什么网站要使用安全证书 我们所处的网络环境是复杂多样的&#xff0c;大致分为两类&#xff0c;一类是可信的网络服务商&#xff0c;比如直接连的电信运营商的网络&#xff0c;网线&#xff0c;4G&#xff0c;5G&#xff1b;另一类是不可信的网络&#xff0c;比如WIFI&am…

Linux笔记之diff工具软件P4merge的使用

Linux笔记之diff工具软件P4merge的使用 code review! 文章目录 Linux笔记之diff工具软件P4merge的使用1.安装和配置2.使用&#xff1a;p4merge a.cc b.cc3.配置git 参考博文: Ubuntu Git可视化比较工具 P4Merge 的安装/配置及使用 1.安装和配置 $ wget https://cdist2.per…

仿CSGO盲盒开箱源码 盲盒商城源码 盲盒开箱源码 潮物盲盒商城源码

仿CSGO盲盒开箱源码 盲盒商城源码 盲盒开箱源码 潮物盲盒商城源码 测试环境&#xff1a;宝塔、Linux、PHP7.2、MySQL5.6 根目录 public&#xff0c;伪静态 thinkphp&#xff0c;php需要Redis扩展 后台&#xff1a;/stf 账号&#xff1a;admin 密码&#xff1a;123123 *后台…

数据结构: map与set的简单实现

目录 map与set的模拟实现 1.基本框架 2.模拟实现map与set所需要做的事 1.使用模板 , 达到泛性编程 2.比较问题 3.迭代器 RBTree中: operator operator-- 4.map [ ] 的实现 5.使用普通迭代器构造const迭代器 效果 map与set的模拟实现 1.基本框架 map set 2.模拟实…

【iOS】MVC模式

MVC&#xff08;Model-View-Controller&#xff0c;模型-视图-控制器&#xff09;模式是相当古老的设计模式之一&#xff0c;ta最早出现在SmallTalk语言中。现在&#xff0c;很多计算机语言和架构都采用了MVC模式。 MVC模式概述 MVC模式是一种设计模式&#xff0c;由3部分组成…

【Spring Cloud】如何确定微服务项目的Spring Boot、Spring Cloud、Spring Cloud Alibaba的版本

文章目录 1. 版本选择2. 用脚手架快速生成微服务的pom.xml3. 创建一个父工程4. 代码地址 本文描述如何确定微服务项目的Spring Boot、Spring Cloud、Spring Cloud Alibaba的版本。 1. 版本选择 我们知道Spring Boot、Spring Cloud、Spring Cloud Alibaba的版本选择一致性非常重…

【ML】cheatsheet

LR 原理与面试题目DT, Adaboost, GBDT, xgboost 原理 细节 与 例子 https://www.cnblogs.com/createMoMo/p/12635709.html xgboost挺详细的算法原理与例子 https://zhuanlan.zhihu.com/p/660468945 着重lightgbm就xgboost的改善方向 https://zhuanlan.zhihu.com/p/366952043机器…

《计算机视觉中的多视图几何》笔记(14)

14 Affine Epipolar Geometry 本章主要是在仿射摄像机的情况下重新考虑对极几何&#xff0c;也就是仿射对极几何。 仿射摄像机的优点是它是线性的&#xff0c;所以很多最优化算法可以用线性代数的知识解决。如果是一般的投影摄像机&#xff0c;很多算法就不是线性的了&#x…