最简单得方法解决TCP分包粘包问题

news2024/12/22 22:02:14

如何用最简单的方法解决TCP传输中的分包粘包问题?

在这里插入图片描述
首先需要说明一点,分包粘包等等一系列的问题并不是协议本身存在的问题,而是程序员在写代码的时候,没有搞清楚数据的边界导致的。

看个简单的例子,TCP客户端不断的向服务器发送字符串,每次发送完成随机睡眠一会。

char *buf[] = {
    "aaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "bbbbbbbbbbb",
    "ccccccccccccccccccc",
    "dddddddddddddddddddddddddddddddddddddddddddd",
    "eeeeeeeeeeeeeeeeeeeeeeee",
    "ffffffff",
    "gggggggggggggggggggggggggggggggggggg",
    "hhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
    "iii",
    "jjjjjjj",
    "kkkkkkkkkkkkkkkkkkkkkk"
};

srand(time(NULL));

for (int i = 0; i < sizeof(buf) / sizeof(buf[0]); i++)
{   
    if (send(sockfd, buf[i], strlen(buf[i]), 0) == -1)
    {
        perror("send");
        break;
    }
    usleep(1000 * 10);
}

服务器端接收数据的时候同样如此。

char buf[1024] = {0};

srand(time(NULL));

while (1) 
{   
    size = recv(fd, buf, sizeof(buf), 0); 
    if (size == -1) 
    {   
        perror("recv");
        break;
    }   
    else if (size == 0)
    {
        printf("客户端断开连接 ...\n");
        break;
    }

    printf("收到一条数据 %s\n", buf);

    bzero(buf, 1024);

    usleep(1000 * (rand() % 100 + 1));
}

我们希望看到的现象是,服务器端收到的数据和客户端一样。

运行程序,客户端发送完成,但是服务器端收到的数据却不是我们想要的。

root@Turbo:test# ./1.tcp-server 
等待客户端的连接 ...
接受客户端的连接 4
收到一条数据 aaaaaaaaaaaaaaaaaaaaaaaaaaaa
收到一条数据 bbbbbbbbbbbcccccccccccccccccccddddddddddddddddddddddddddddddddddd
dddddddddeeeeeeeeeeeeeeeeeeeeeeeeffffffffgggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiijjjjjjj收到一条数据 kkkkkkkkkkkkkkkkkkkkkk
客户端断开连接 ...
root@Turbo:test#

数据内容没有变,出现了多个字符串连接在一起的现象。

原因就是发送数据过快,或者接收数据太慢,导致TCP缓冲区中积累了很多数据,调用recv函数读数据的时候,就会一下子全部读出来。

想要解决这个问题,最简单的办法就是分清楚数据包的边界。发送字符串之前,在数据包的前面加上字符串的长度。

char *sendMsg = (char *)malloc(1024);
int len = 0;

srand(time(NULL));

for (int i = 0; i < sizeof(buf) / sizeof(buf[0]); i++)
{
    len = strlen(buf[i]);
    memcpy(sendMsg, &len, sizeof(int));
    memcpy(sendMsg + sizeof(int), buf[i], len);

    if (send(sockfd, sendMsg, strlen(buf[i]) + sizeof(int), 0) == -1)
    {
        perror("send");
        break;
    }
    memset(sendMsg, 0, 1024);

    usleep(1000 * 10);
}

接收数据的时候,先读取4个字节的整型数据,得到接下来字符串的长度,再读取对应长度的字符串。

char buf[1024] = {0};
ssize_t size;
int len = 0;

srand(time(NULL));

while (1) 
{   
    size = recv(fd, &len, sizeof(int), 0); 

    size = recv(fd, buf, len, 0); 
    if (size == -1) 
    {   
        perror("recv");
        break;
    }
    else if (size == 0)
    {
        printf("客户端断开连接 ...\n");
        break;
    }

    printf("收到一条数据 %s\n", buf);

    bzero(buf, 1024);

    usleep(1000 * (rand() % 100 + 1));
}

再次运行程序,不管睡眠时间怎么变化,服务器端收到的数据和客户端一样,也没有出现粘在一起的现象。

root@Turbo:test# ./1.tcp-server 
等待客户端的连接 ...
接受客户端的连接 4
收到一条数据 aaaaaaaaaaaaaaaaaaaaaaaaaaaa
收到一条数据 bbbbbbbbbbb
收到一条数据 ccccccccccccccccccc
收到一条数据 dddddddddddddddddddddddddddddddddddddddddddd
收到一条数据 eeeeeeeeeeeeeeeeeeeeeeee
收到一条数据 ffffffff
收到一条数据 gggggggggggggggggggggggggggggggggggg
收到一条数据 hhhhhhhhhhhhhhhhhhhhhhhhhhhhh
收到一条数据 iii
收到一条数据 jjjjjjj
收到一条数据 kkkkkkkkkkkkkkkkkkkkkk

方法很简单,也只是加了一个包头,其实目的就是为了告诉接收端,数据包从哪开始,到哪结束,这样就算缓冲区中有大量数据,也能分得清楚。

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

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

相关文章

SAP 解析固定资产的减值功能

SAP固定资产的减值功能 若固定资产出现减值迹象&#xff0c;也就是固定资产的可收回金额小于账面价值时&#xff0c;就要计提固定资产减值准备。 分录&#xff1a; 借&#xff1a;资产减值损失&#xff08;损益科目&#xff09; 贷&#xff1a;固定资产减值准备&#xff08;资…

骨传导耳机是不是智商税?骨传导耳机真的不伤耳吗?

很多人对骨传导耳机是具有一定的了解&#xff0c;但是对骨传导耳机还是有一定的刻板印象&#xff0c;那么骨传导耳机到底是不是智商税呢&#xff1f;主要还是要从骨传导耳机传声原理上讨论。 骨传导耳机是属于固体传声的一种方式&#xff0c;通过骨骼传递声音&#xff0c;在使用…

一种基于强化学习的自动变道机动方法

文章目录摘要前言相关的工作方法论动作空间奖励函数设计Q学习仿真结果结论摘要 变道是一项至关重要的车辆操作&#xff0c;需要与周围车辆协调。建立在基于规则的模型上的自动换道功能可能在预定义的操作条件下表现良好&#xff0c;但在遇到意外情况时可能容易失败。在我们的研…

谈一谈正向代理和反向代理?

谈一谈正向代理和反向代理&#xff1f;什么是代理服务器&#xff08;Proxy Serve&#xff09;&#xff1f;为什么使用代理服务器&#xff1f;什么是正向代理什么是反向代理正向代理和反向代理的区别正向代理的应用反向代理的应用什么是代理服务器&#xff08;Proxy Serve&#…

android kotlin 协程(四) 协程间的通信

android kotlin 协程(四) 协程间的通信 学完本篇你将会了解到: channelproduceactorselect 先来通过上一篇的简单案例回顾一下挂起于恢复: fun main() {val waitTime measureTimeMillis {runBlocking<Unit> {println("main start") // 1 // …

学会这些Jmeter插件,才能设计出复杂性能测试场景

为什么要使用jmeter线程组插件呢&#xff1f; jmeter自带的线程组插件模拟的压测场景非常有限&#xff0c;当需要模拟复杂压测场景的时候&#xff0c;推荐大家使用jmeter线程组插件。 如何下载jmeter线程组插件呢&#xff1f; 早期版本的jmeter可以针对我们需要的扩展功能&a…

软考案例分析题精选

试题一&#xff1a;阅读下列说明&#xff0c;回答问题1至问题4&#xff0c;将解答填入答题纸的对应栏内。某公司中标了一个软件开发项目&#xff0c;项目经理根据以往的经验估算了开发过程中各项任务需要的工期及预算成本&#xff0c;如下表所示&#xff1a;任务紧前任务工期PV…

大规模 IoT 边缘容器集群管理的几种架构-1-Rancher+K3s

前文回顾 大规模 IoT 边缘容器集群管理的几种架构-0-边缘容器及架构简介 &#x1f4da;️Reference: IoT 边缘计算系列文章 Rancher K3s 简介 Rancher&#xff1a; Kubernetes 统一管理平台&#xff0c; Rancher 是为采用容器的团队提供的一个完整的软件栈。它解决了管理多个…

PCI设备驱动初探(仅仅是内核部分,不是具体设备驱动)

在操作系统中&#xff0c;声卡、网卡之类的设备驱动并不像硬盘、鼠标、键盘等等驱动直接编写就行了。它们是建立在内核PCI驱动基础上的&#xff0c;也就是说这类设备通过PCI总线与系统通信。所以要编写这类的驱动首先要构造一个PCI设备的内核驱动&#xff0c;这样我们才能继续正…

Hive学习——DDLDML语句

目录 一、Hive数据类型 (一)Hive基本数据类型 (二)Hive的基本数据类型转换 (三)Hive集合数据类型 (四)文本文件数据编码 (五)读时模式 (六)Hive数据结构 二、DDL&DML命令 (一)数据库操作 1.创建数据库 2.查看数据库 3.修改数据库 4.删除数据库 5.切换(使用)指…

【LeetCode】No.225. 用队列实现栈 -- Java Version

题目链接&#xff1a;https://leetcode.cn/problems/implement-stack-using-queues/ 1. 题目介绍&#xff08;225. 用队列实现栈&#xff09; 请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、t…

回文子串的数量[寻找回文子串的完整思路过程]

寻找回文子串的完整思路过程前言一、回文串的数量二、动态规划1、完整思考过程2、go总结参考文献前言 回文字符串&#xff0c;就是从左遍历和从右遍历的字符是相同顺序的&#xff0c;转换一下&#xff0c;就是该字符串是对称的。寻找回文子串面临两个直接的问题&#xff0c;1-…

pytorch深度学习案例(二)——航拍街道语义分割

数据集 使用的数据集是kaggle的Semantic segmentation of aerial imagery 其数据的组织形式为 项目结构 utils dataConvert.py dataConvert中主要包含数据的变换过程 函数作用loadColorMap用于加载标签的颜色映射voc_colormap2label获取颜色标签到数值标签的映射关系voc_…

黑马Spring学习笔记(二)——注解开发

目录 一、纯注解开发 1.1 实现步骤 1.2 小结 二、注解开发依赖注入 2.1 自动装配 2.1.1 Autowired——按照类型注入 2.1.2 Qualifier——按照名称注入 2.1.3 Value——简单类型注入 2.2 注解读取properties配置文件——PropertySource 三、注解开发管理第三方B…

【基础算法】差分

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…

Kotlin 33. CompileSdkVersion 和 targetSdkVersion 有什么区别?

CompileSdkVersion 和 targetSdkVersion 有什么区别&#xff1f; 在 build.gradle (Module) 文件中&#xff0c;我们通常会看到 CompileSdkVersion 和 targetSdkVersion 的使用&#xff0c;比如下面是一个完整的 build.gradle (Module) 文件&#xff1a; plugins {id com.and…

JavaScript随手笔记---比较两个数组差异

&#x1f48c; 所属专栏&#xff1a;【JavaScript随手笔记】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#…

0基础学习diffusion_model扩散模型【易理解的公式推导】

0基础学习diffusion_model扩散模型【易理解的公式推导】一、概述二、扩散过程(已知X0求Xt)三、逆扩散过程(已知Xt求Xt-1)1。算法流程图四、结论五、损失函数六、心得体会&#xff08;优缺点分析&#xff09;一、概述 DDPM论文链接&#xff1a; Jonathan Ho_Denoising Diffusion…

Android入门第64天-MVVM下瀑布流界面的完美实现-使用RecyclerView

前言 网上充满着不完善的基于RecyclerView的瀑布流实现&#xff0c;要么根本是错的、要么就是只知其一不知其二、要么就是一充诉了一堆无用代码、要么用的是古老的MVC设计模式。 一个真正的、用户体验类似于淘宝、抖音的瀑布流怎么实现目前基本为无解。因为本人正好自己空闲时也…