c++处理tcp粘包问题以及substr方法

c++处理tcp粘包问题以及substr方法

  • 1.粘包原因
  • 2.tcp基础
    • 三次握手
    • 四次挥手
    • 长连接和和短连接
  • 3.解决方式
    • 1.定长消息:
    • 2.分隔符消息:
  • 4.substr方法

1.粘包原因

在TCP通信中,粘包是指发送方在发送数据时,多个小的数据包被合并成一个大的数据包,或者接收方在接收数据时,一个大的数据包被拆分成多个小的数据包。这种情况可能会导致接收方无法正确解析数据,从而造成数据处理错误。

2.tcp基础

TCP是一种面向连接的协议,它提供可靠的、有序的、基于字节流的数据传输。TCP建立连接的过程包括“三次握手”,即客户端发送连接请求,服务器回应确认,最后客户端再次回应确认。连接建立后,TCP通过使用滑动窗口、序列号和确认机制来保证数据的顺序和完整性。TCP还支持流量控制和拥塞控制机制,以保证网络的可靠性和稳定性。因此,TCP适用于需要可靠传输的应用,如网页浏览、文件传输、电子邮件等。

三次握手

1.第一次握手:客户端发送连接请求报文段(SYN)到服务器,进入SYN_SENT状态。
2.第二次握手:服务器收到请求后,回复一个确认报文段(SYN+ACK)以及自己的连接请求报文段(SYN),进入SYN_RCVD状态。
3.第三次握手:客户端收到确认后,再发送一个确认报文段(ACK),双方进入Established状态,连接建立。
三次握手的主要目的是双方确认彼此的发送和接收能力正常,并且同步初始序列号。

四次挥手

1.第一次挥手:发起关闭的一方发送一个FIN报文段给对方,进入FIN_WAIT_1状态。
2.第二次挥手:对方收到FIN后,回复一个确认报文段(ACK),进入CLOSE_WAIT状态。
3.第三次挥手:当对方不再需要发送数据时,会发送一个FIN报文段给发起关闭的一方,进入LAST_ACK状态。
4.第四次挥手:发起关闭的一方收到对方的FIN后,发送一个确认报文段(ACK),进入TIME_WAIT状态。等待2MSL时间后,进入CLOSED状态。
四次挥手的主要目的是确保双方都能够完成未发送完的数据的传输,并且结束连接。

长连接和和短连接

  • 长连接:指在一个TCP连接中可以传输多个数据包,在处理完一个请求后不会立即断开连接,而是保持连接状态,等待后续的请求。长连接可以减少连接建立和断开的开销,适用于频繁的数据交换场景,如网页浏览、移动App等。
  • 短连接:指每次请求都要建立一个新的TCP连接,在请求处理完毕后立即断开连接。短连接适用于一次性传输少量数据的场景,如DNS查询、文件下载等。
    选择长连接还是短连接取决于具体的应用场景和性能要求。

3.解决方式

解决TCP粘包问题有多种方法,下面介绍两种常用的方式:

1.定长消息:

发送方在发送数据时,将每个数据包的长度固定为一个固定值。接收方在接收数据时,根据固定的长度对数据进行拆分。这种方式简单直接,但是对于不同长度的数据包处理不便。

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
const int MESSAGE_LENGTH = 10;  // 定义消息长度为10
int main() {
// 创建socket
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket == -1) {
std::cerr << "Failed to create socket" << std::endl;
return -1;
}
// 设置socket地址
sockaddr_in serverAddress{};
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(8080);
serverAddress.sin_addr.s_addr = INADDR_ANY;// 绑定socket地址
if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {std::cerr << "Failed to bind socket" << std::endl;close(serverSocket);return -1;
}// 监听socket
if (listen(serverSocket, SOMAXCONN) == -1) {std::cerr << "Failed to listen on socket" << std::endl;close(serverSocket);return -1;
}while (true) {// 接受新的连接sockaddr_in clientAddress{};socklen_t clientAddressSize = sizeof(clientAddress);int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &clientAddressSize);if (clientSocket == -1) {std::cerr << "Failed to accept connection" << std::endl;close(serverSocket);return -1;}std::cout << "New connection accepted" << std::endl;// 接收数据char buffer[MESSAGE_LENGTH];std::string receivedData;while (true) {memset(buffer, 0, sizeof(buffer));ssize_t bytesRead = recv(clientSocket, buffer, sizeof(buffer), 0);if (bytesRead == -1) {std::cerr << "Failed to receive data" << std::endl;close(clientSocket);break;}if (bytesRead == 0) {std::cout << "Connection closed" << std::endl;close(clientSocket);break;}receivedData += buffer;// 检查是否有完整的消息while (receivedData.length() >= MESSAGE_LENGTH) {std::string message = receivedData.substr(0, MESSAGE_LENGTH);std::cout << "Received message: " << message << std::endl;// 处理消息// 移除已处理的消息receivedData = receivedData.substr(MESSAGE_LENGTH);}}
}// 关闭socket
close(serverSocket);return 0;
}

2.分隔符消息:

发送方在发送数据时,在每个数据包的末尾添加一个特定的分隔符,例如换行符或特殊字符。接收方在接收数据时,根据分隔符将数据包拆分成多个小的数据包。这种方式对于不同长度的数据包处理更加灵活,但需要注意选择合适的分隔符,以避免与数据内容冲突。

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstdlib>
#include <cstring>const char DELIMITER = '\n';int main() {// 创建socketint serverSocket = socket(AF_INET, SOCK_STREAM, 0);if (serverSocket == -1) {std::cerr << "Failed to create socket" << std::endl;return -1;}// 设置socket地址sockaddr_in serverAddress{};serverAddress.sin_family = AF_INET;serverAddress.sin_port = htons(8080);serverAddress.sin_addr.s_addr = INADDR_ANY;// 绑定socket地址if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {std::cerr << "Failed to bind socket" << std::endl;close(serverSocket);return -1;}// 监听socketif (listen(serverSocket, SOMAXCONN) == -1) {std::cerr << "Failed to listen on socket" << std::endl;close(serverSocket);return -1;}while (true) {// 接受新的连接sockaddr_in clientAddress{};socklen_t clientAddressSize = sizeof(clientAddress);int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &clientAddressSize);if (clientSocket == -1) {std::cerr << "Failed to accept connection" << std::endl;close(serverSocket);return -1;}std::cout << "New connection accepted" << std::endl;// 接收数据char buffer[1024];std::string receivedData;while (true) {memset(buffer, 0, sizeof(buffer));ssize_t bytesRead = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);if (bytesRead == -1) {std::cerr << "Failed to receive data" << std::endl;close(clientSocket);break;}if (bytesRead == 0) {std::cout << "Connection closed" << std::endl;close(clientSocket);break;}receivedData += buffer;// 检查是否有完整的消息size_t delimiterPos = receivedData.find(DELIMITER);while (delimiterPos != std::string::npos) {std::string message = receivedData.substr(0, delimiterPos);std::cout << "Received message: " << message << std::endl;// 处理消息// 移除已处理的消息receivedData = receivedData.substr(delimiterPos + 1);// 继续查找下一个分隔符delimiterPos = receivedData.find(DELIMITER);}}}// 关闭socketclose(serverSocket);return 0;
}

4.substr方法

C++ 标准库中的一个字符串处理函数,用于从一个字符串中提取子字符串。
substr 函数的语法如下:

string substr(size_t pos = 0, size_t len = npos) const;

其中,pos 参数表示要提取的子字符串的起始位置,len 参数表示要提取的子字符串的长度。如果不指定 len 参数,则默认提取从 pos 位置到字符串末尾的所有字符。
substr 函数返回一个新的字符串,包含了从原始字符串中提取的子字符串。
下面是一个简单的示例,演示了如何使用 substr 函数:

#include <iostream>
#include <string>int main() {std::string str1 = "Hello, World!";// 提取从位置 7 开始的子字符串std::string substr1 = str1.substr(7);std::cout << "Substring 1: " << substr1 << std::endl;// 提取从位置 0 开始,长度为 5 的子字符串std::string substr2 = str1.substr(0, 5);std::cout << "Substring 2: " << substr2 << std::endl;std::cout << sizeof(str1) << std::endl;//清空定长str1str1 = str1.substr(7);//清空所有//str1.clear();//str1 = "";std::cout << "str1: " << str1 << std::endl;return 0;
}

输出:

Substring 1: World!
Substring 2: Hello
str1:World!

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

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

相关文章

Python基础入门----如何通过conda搭建Python开发环境

文章目录 使用 conda 搭建Python开发环境是非常方便的,它可以帮助你管理Python版本、依赖库、虚拟环境等。以下是一个简单的步骤,演示如何通过 conda 搭建Python开发环境: 安装conda: 如果你还没有安装 conda,首先需要安装Anaconda或Miniconda。Anaconda是一个包含很多数据…

pythom导出mysql指定binlog文件

要求 要求本地有py环境和全局环境变量 先测试直接执行binlog命令执行命令 Windows 本地直接执行命令 # E:\output>E:\phpstudy_pro\Extensions\MySQL5.7.26\bin\mysqlbinlog binglog文件地址 # --no-defaults 不限制编码 # -h mysql链接地址 # -u mysql 链接名称 # -p m…

牛掰的dd命令,cpi0配合find备份(不会主动备份),od查看

dd if设备1或文件 of设备2或文件 blocknsize countn 还原就是把设备1,2调过来 这里想到dump的还原是命令restore&#xff0c;想起来就写一下&#xff0c;省的总忘记 可以针对整块磁盘进行复制&#xff0c;对于新创建的分区&#xff0c;也不用格式化&#xff0c;可以直接…

高防CDN为什么可以防DDOS攻击

CDN的全称是ContentDeliveryNetwork&#xff0c;即内容分发网络&#xff0c;顾名思义&#xff0c;它是一个分布式节点网络(也称为边缘服务器)&#xff0c;CDN节点具有缓存内容的功能&#xff0c;使用户可以在不获取源服务器数据的情况下就近获取所需内容&#xff0c;提高客户访…

MyBatis在注解中使用动态查询

以前为了使用注解并在注解中融入动态查询&#xff0c;会使用Provider。后来发现只要加入"<script>包含动态查询的SQL语句</script>"就可以了。 例如&#xff1a; Select("<script>" "select v.*,u.avatar,u.nickname from videos…

Androidstudio中build.gradle classpath如何添加

Androidstudio中build.gradle classpath如何添加 build.gradle classpath如何添加 build.gradle classpath如何添加 升级as之后&#xff0c;gradle版本也升级了&#xff0c;导致project的build.gradle中的写法也不一样了。 buildscript {repositories {google()mavenCentral(…

SQL引擎子系统的工作原理

SQL引擎子系统负责将用户发送过来的SQL请求进行解析&#xff0c;语义检查&#xff0c;生成逻辑计划&#xff0c;经过一系列重写和优化&#xff0c;生成物理计划&#xff0c;交由计划执行器执行。 SQL语句分为两大类。一是数据操作语言&#xff0c;包括 select update insert de…

Azure Machine Learning - Azure AI 搜索中的集成数据分块和嵌入

在基于索引器的索引编制中&#xff0c;Azure AI _集成矢量化_将数据分块和文本到矢量嵌入添加到技能中&#xff0c;它还为查询添加文本到矢量的转换。 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;同济本…

【Java程序员面试专栏 算法训练篇】二叉树高频面试算法题

一轮的算法训练完成后,对相关的题目有了一个初步理解了,接下来进行专题训练,以下这些题目就是二叉树相关汇总的高频题目 遍历二叉树 遍历二叉树,分为递归和迭代两种方式,递归类似于DFS,迭代类似于BFS,【算法训练-二叉树 一】【遍历二叉树】前序遍历、中序遍历、后续遍…

Godot Shader -变量的声明

变量的声明 uniform 可以将值传递给着色器。这些值对整个着色器来说是全局的&#xff0c;被称为 uniform。当一个着色器后来被分配给一个材质时&#xff0c;uniform 将作为可编辑的参数出现在其中。uniform 不能从着色器内部写入。 可以在材质编辑器的修改这些uniform的值&a…

力扣-414.第三大的数(两种解法)

文章目录 第三大的数解法一&#xff08;排序加遍历对比&#xff09;解法二&#xff08;遍历一遍加迭代&#xff09; 第三大的数 题目&#xff1a; 给你一个非空数组&#xff0c;返回此数组中第三大的数 。如果不存在&#xff0c;则返回数组中最大的数。 示例 1&#xff1a; 输…

YOLOv8-seg改进:SEAM、MultiSEAM分割物与物相互遮挡、分割小目标性能

🚀🚀🚀本文改进:SEAM、MultiSEAM分割物体与物体相互遮挡性能 🚀🚀🚀SEAM、MultiSEAM分割物与物相互遮挡、分割小目标性能 🚀🚀🚀YOLOv8-seg创新专栏:http://t.csdnimg.cn/KLSdv 学姐带你学习YOLOv8,从入门到创新,轻轻松松搞定科研; 1)手把手教你如何…

Java值传递和引用传递

在Java中&#xff0c;有值传递&#xff08;Pass-by-Value&#xff09;和引用传递&#xff08;Pass-by-Reference&#xff09;两种参数传递方式。 值传递&#xff08;Pass-by-Value&#xff09;&#xff1a;当使用值传递方式时&#xff0c;方法将参数的副本传递给调用方法。这意…

常见的面试算法题:阶乘、回文、斐波那契数列

1.阶乘算法 Factorial 例如&#xff1a;给出数字5&#xff0c;对其以下的的每个数字相乘&#xff0c;结果等于120 解&#xff1a;递归 Recursive function factorial(n) {// 如果n为0或1&#xff0c;阶乘是1if (n 0 || n 1) {return 1;}// 否则&#xff0c;返回n乘以n-1的…

whisper使用方法

看这个 github https://github.com/Purfview/whisper-standalone-win/tags下载 视频提取音频 ffmpeg -i 222.mp4 -vn -b:a 128k -c:a mp3 output.mp3截取4秒后的音频 ffmpeg -i output.mp3 -ss 4 -c copy output2.mp3使用 whisper-faster.exe 生成字幕 whisper-faster.exe …

卷积神经网络(VGG-19)灵笼人物识别

文章目录 前期工作1. 设置GPU&#xff08;如果使用的是CPU可以忽略这步&#xff09;我的环境&#xff1a; 2. 导入数据3. 查看数据 二、数据预处理1. 加载数据2. 可视化数据3. 再次检查数据4. 配置数据集5. 归一化 三、构建VGG-19网络1. 官方模型&#xff08;已打包好&#xff…

Zotero在word中插入带超链接的参考文献/交叉引用/跳转参考文献

Zotero以其丰富的插件而闻名&#xff0c;使用起来十分的带劲&#xff0c;最重要的是它是免费的、不卡顿&#xff0c;不像某专业软件。 然而Zotero在word插入参考文献时&#xff0c;无法为参考文献添加超链接&#xff0c;这是一个不得不提的遗憾。 不过&#xff0c;有大佬已经…

Windows SDK

Windows SDK (10.0.22621) for Windows 11&#xff0c;版本 22H2 (2023 年 10 月更新) 提供了用于生成 Windows 应用程序的最新标头、库、元数据和工具。 使用此 SDK 为Windows 11版本 22H2 和早期 Windows 版本生成通用 Windows 平台 (UWP) 和 Win32 应用程序。 Windows 应用…

华纳云服务器怎么清理cdn缓存?

清理 CDN(内容分发网络)缓存通常需要通过 CDN 提供商的管理界面或 API 进行操作。不同的 CDN 提供商可能有不同的方法和步骤&#xff0c;以下是一个通用的清理 CDN 缓存的一般步骤&#xff1a; 1. 登录到 CDN 提供商的管理界面&#xff1a; 打开你所使用的 CDN 提供商的网站。 …

武汉凯迪正大KDHG-220P互感器综合测试仪

主要特点 武汉凯迪正大KDHG-220P互感器综合测试仪&#xff0c;仅需进行简单的数字设定&#xff1a;设定互感器的额定参数。仪器将全过程自动记录数据&#xff0c;并自动将变比极性、伏安特性曲线等计算并显示出来&#xff0c;省去换线、手动调压、人工记录、整理、描曲线等烦琐…