tcp 粘包和拆包 及 解决粘包方案
什么是粘包和拆包
.TCP 是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的 socket,因此,发送端为了将多个发给接收端的包,更有效的发给对方,使用了优化方法(Nagle 算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样做虽然提高了效率,但是接收端就难于分辨出完整的数据包了,因为面向流的通信是无消息保护边界的
粘包和拆包是网络编程中常遇到的问题,主要是因为TCP协议的Nagle算法和接收方缓冲区的不足导致的。
粘包:发送方发送的若干包数据到接收方接收时粘在一起,导致数据不能正确分割。
拆包:发送方发送的包数据在接收方解析时被拆开,导致数据不能正确解析。
客户端
直接发消息
第一种方式发消息的时候 每一条消息的结束时候以特殊符合作为结束标志 例如#、\n等符号
// 其他端拿到数据之后 对数据进行特殊符号的分割
TcpClient client = new TcpClient();
client.Connect("端口号", 8080);
NetworkStream stream = client.GetStream();
Send(stream, "三十而立,指的是拥有了30个重装合成旅,才能在这个星球上拥有立足的资本");
Send(stream, "四十而不惑,指的是拥有40个重装合成旅,这个星球上就没有能让我们感到困惑的事");
Send(stream, "五十而知天命,指的是拥有50个重装合成旅,这个星球的其他国家便能知道我们所说的话便是他的天命");
第二种发送数据时候 出现粘包现象,后台处粘包
//包头当中添加数据长度解决粘包
void Send(NetworkStream stream,string msg)
{//1定义一个字节数组获取发送数据字节流byte[] bs = Encoding.UTF8.GetBytes(msg);//2 创建一个内存流,存在内存当中,可以理解为虚拟的文件流MemoryStream ms = new MemoryStream();//3 创建一个二进制读写对象 写入内存流BinaryWriter bw = new BinaryWriter(ms);//4写入数据的长度的bw.Write(bs.Length);//5 写入数据字节数组bw.Write(bs);//6 发送数据 把数据长度和数据内容同时发给服务器stream.Write(ms.ToArray(), 0, ms.ToArray().Length);bw.Close();ms.Close();}
服务器:
Server封装:
TcpListener listen;
public Server(IPAddress ip,int port)
{listen = new TcpListener(ip, port);
}public void Start()
{listen.Start(100);StartConnect();
}
Dictionary<string,TcpClient> clientDic = new Dictionary<string,TcpClient>();
public event Action<TcpClient> 有客户端连入的事件;
void StartConnect()
{Task.Run(() =>{while (true) {TcpClient client = listen.AcceptTcpClient();string ip = client.Client.RemoteEndPoint.ToString(); clientDic.Add(ip, client);有客户端连入的事件?.Invoke(client);startRead(client);//读取粘包的数据,在这个方法进行拆包处理}});
}
拆包
public void startRead(TcpClient t1)
{//接受客户端发来的数据NetworkStream stream = t1.GetStream();string ip = t1.Client.RemoteEndPoint.ToString();byte[] bs = new byte[1024 * 1024];Task.Run(() =>{try{while (true){int count= stream.Read(bs, 0, bs.Length);if (count== 0){throw new Exception("客户端断了");}//接受数据byte[] body = bs.Take(count).ToArray();string s = Encoding.UTF8.GetString(body);Console.WriteLine("-------------------------------");Console.WriteLine("接收到消息为:"+s);Console.WriteLine("-------------------------------");if (上一个数据半包 != null) //判断是否有半包 如果有,把半包取出来和当前包合并{body = 上一个数据半包.Concat(body).ToArray();//把俩个数组合成一个数组上一个数据半包 = null; //清空半包}//开始拆包 封装一个拆包方法//参数1 当前要拆的数据//参数2 第几个位置开始拆//参数3 当前客户端ChaiBao(body, 0, t1);}}catch (Exception ex){clientDic.Remove(ip);}});
}
byte[] 上一个数据半包 = null;public void ChaiBao(byte[] bytes ,int startIndex, TcpClient client){if(startIndex+4>bytes.Length) {//如果开始位置加上4大于该数据包的长度时候 说明当前包是一个半包//跳过之前元素,取出剩余的数据上一个数据半包 = bytes.Skip(startIndex).ToArray();return;}//计算第一个包长度 获取从开始到startIndex之间的长度int len = BitConverter.ToInt32 (bytes,startIndex);// 取出对应位置包的总体大小// 之前的包长度总和int abc = len + startIndex + 4;//判断当前包是整包还是半包或者是多包if (abc == bytes.Length) { //如果之前所有包的大小加上当前数据包大小等于全包的长度,证明当前是一个整包byte[] bs1 = bytes.Skip(startIndex+4).ToArray();接受到消息的事件?.Invoke(client, bs1);}else if (abc < bytes.Length){byte[] bs2 = bytes.Skip(startIndex+4).Take(len).ToArray();接受到消息的事件?.Invoke(client, bs2);//如果之前包加上当前包小于全包的长度,说明当前包是多包。如果是多包,再进行拆包ChaiBao(bytes, abc, client);}else{ //目前是一个半包上一个数据半包 = bytes.Skip (startIndex).ToArray();}}
群发方法
public event Action<string> 客户端断开事件; public event Action<TcpClient, byte[]> 接受到消息的事件;public Server(){}//群发方法 向所有的客户端发消息public void Send(string content){byte[] bs = Encoding.UTF8.GetBytes(content);foreach (var item in clientDic) //遍历所有的客户端{item.Value.GetStream().Write(bs, 0, bs.Length);}}//指定给谁发public void Send(string content,string ip) {byte[] bs = Encoding.UTF8.GetBytes(content);//根据ip取出客户端,从字典取clientDic[ip].GetStream().Write(bs, 0, bs.Length);}//指定给哪些客户端发//send("你好", ["192.","127"])public void Send(string content, string[] ips){byte[] bs = Encoding.UTF8.GetBytes(content);foreach (var item in clientDic) //所有客户端{//item.key 键 ip字符串//item.value 值 客户端对象if (ips.Contains(item.Key)){//如果ips数组包含目标客户端item.Value.GetStream().Write(bs, 0, bs.Length);}}}
Program
static Server s;static void Main(string[] args){s = new Server(IPAddress.Any,8080);s.有客户端连入的事件 += f1;s.接受到消息的事件 += f2;s.Start();Console.ReadKey();}public static void f1(TcpClient t1){Console.WriteLine(t1.Client.RemoteEndPoint.ToString()+"连接到服务器");}public static void f2(TcpClient t2, byte[] bs){Console.WriteLine(t2.Client.RemoteEndPoint.ToString()+"发来的消息为+++++++++++:"+ Encoding.UTF8.GetString(bs,0,bs.Length));}相关文章:
tcp 粘包和拆包 及 解决粘包方案
什么是粘包和拆包 .TCP 是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的 socket,因此,发送端为了将多个发给接收端的包,更有效的发给对方,使…...
【2024泰迪杯】B 题:基于多模态特征融合的图像文本检索20页论文及Python代码
【2024泰迪杯】B 题:基于多模态特征融合的图像文本检索20页论文及Python代码 相关链接 【2024泰迪杯】A 题:生产线的故障自动识别与人员配置 Python代码实现 【2024泰迪杯】B 题:基于多模态特征融合的图像文本检索Python代码实现 【2024泰迪…...
华为设备telnet 远程访问配置实验简述
一、实验需求: 1、AR1模拟电脑telnet 访问AR2路由器。 二、实验步骤: 1、AR1和AR2接口配置IP,实现链路通信。 2、AR2配置AAA模式 配置用户及密码 配置用户访问级别 配置用户telnet 访问服务 AR2配置远程服务数量 配置用户远程访问模式为AAA 配置允许登录…...
在HTML中,如何正确使用语义化标签?
在HTML中,使用语义化标签可以使得网页结构更加清晰和易于理解。以下是一些正确使用语义化标签的方法: 使用合适的标题标签(h1-h6)来标识网页的标题,以及页面中的各个区块的标题。 <h1>网页标题</h1> <…...
WHAT - 高性能和内存安全的 Rust(一)
目录 一、介绍1.1 示例代码1.2 关键特性内存安全零成本抽象:高效性能示例代码:使用迭代器的零成本抽象示例代码:泛型和单态化总结 并发编程:防止数据竞争Rust 并发编程示例Rust 的所有权系统防止数据竞争总结 丰富的类型系统包管理…...
八、C#运算符
C#运算符 晕杜甫是一种告诉编辑器执行特定的数学或逻辑操作的符号。C#有丰富的内置运算符,分类如下: 算术运算符关系运算符逻辑运算符位运算符赋值运算符其他运算符 算术运算符 下表显示了 C# 支持的所有算术运算符。假设变量 A 的值为 10,…...
【HiveSQL】join关联on和where的区别及效率对比
测试环境:hive on spark spark版本:3.3.1 一、执行时机二、对结果集的影响三、效率对比1.内连接1)on2)where 2.外连接1)on2)where 四、总结PS 一、执行时机 sql连接中,where属于过滤条件&#…...
如何解决windows自动更新,释放C盘更新内存
第一步:首先关闭windows自动更新组件 没有更新windows需求,为了防止windows自动更新,挤占C盘空间,所以我们要采取停止Windows Update服务。按下WinR打开运行对话框,输入services.msc, 然后按Enter。在服务…...
初学51单片机之PWM实例呼吸灯以及遇到的问题(已解答)
PWM全名Pulse Width Modulation中文称呼脉冲宽度调制 如图 这是一个周期10ms、频率是100HZ的波形,但是每个周期内,高低电平宽度各不相同,这就是PWM的本质。 占空比是指高电平占整个周期的比列,上图第一个波形的占空比是40%,第二个…...
手机天线都去哪里了?
在手机的演变历程中,天线的设计和位置一直是工程师们不断探索和创新的领域。你是否好奇,现在的手机为什么看不到那些曾经显眼的天线了呢? 让我们一起揭开这个谜题。 首先,让我们从基础开始:手机是如何发出电磁波的&…...
计算机网络 —— 应用层(电子邮件)
计算机网络 —— 应用层(电子邮件) 电子邮件发送电子邮件的过程SMTP特性工作流程 电子邮件格式MIME关键组件工作方式 POP/IMAPPOP(邮局协议)IMAP(因特网邮件访问协议) 基于万维网的电子邮箱特点优势常见的基…...
Java18新特性(极简)
一、引言 自1995年Java语言首次亮相以来,它已经成为企业级应用、移动应用和游戏开发等领域不可或缺的一部分。随着技术的不断进步,Java也在持续演化,每个新版本都带来了诸多新特性和性能优化,旨在提升开发者的编程效率和应用程序的…...
vscode连接ssh远程服务器
当使用Visual Studio Code (VSCode) 连接SSH远程服务器时,可以遵循以下步骤。这些步骤将帮助你设置并连接到远程服务器,包括免密登录的设置(如果需要)。 一、安装并配置Remote-SSH插件 下载并安装VSCode:确保你已经下…...
【趣味测试】
编程过程中遇到的趣味知识 1 Cpp 1.1 浮点数计算 if (0.1 0.2 0.3) {std::cout << "0.1 0.2 0.3 true" << std::endl;} else {std::cout << "0.1 0.2 0.3 false" << std::endl;}if (0.1 0.3 0.4) {std::cout << &…...
数据结构经典面试之数组——C#和C++篇
文章目录 1. 数组的基本概念与功能2. C#数组创建数组访问数组元素修改数组元素数组排序 3. C数组创建数组访问数组元素修改数组元素数组排序 4. 数组的实际应用与性能优化5. C#数组示例6. C数组示例总结 数组是编程中常用的数据结构之一,它用于存储一系列相同类型的…...
docker的基本知识
文章目录 前言docker的基本知识1. docker 的底层逻辑2. docker 的核心要素2.1. 镜像的基本概念:2.2. 容器的基本概念:2.3. 仓库的基本概念: 前言 如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。 …...
React Native性能优化红宝书
一、React Native介绍 React Native 是Facebook在React.js Conf2015 推出的开源框架,使用React和应用平台的原生功能来构建 Android 和 iOS 应用。通过 React Native,可以使用 JavaScript 来访问移动平台的 API,使用 React 组件来描述 UI 的…...
后端不提供文件流接口,前台js使用a标签实现当前表格数据(数组非blob数据)下载成Excel
前言:开发过程中遇到的一些业务场景,如果第三方不让使用,后端不提供接口,就只能拿到table数据(Array),实现excel文件下载。 废话不多说,直接上代码,方法后续自行封装即可: functio…...
如何使用ChatGPT辅助设计工作
文章目录 设计师如何使用ChatGPT提升工作效率?25个案例告诉你!什么是 prompt?咨询信息型 prompt vs 执行任务 prompt编写出色 prompt 的基本思路撰写 prompt 的案例和技巧1、将 ChatGPT 视作专业人士2、使用 ChatGPT 创建表单3、使用 ChatGPT…...
hadoop服务器启动后无法执行hdfs dfs命令
集群启动后,无法正常使用hdfs的任何命令。使用jps查看进程,发现namenode没有启动,然后再进入到Hadoop的相应目录,打开里面的logs文件 打开Hadoop的master的log 再使用vi编辑器查看(也可以用less或者more命令查看&#…...
imFile下载管理器:从入门到精通的免费全能下载解决方案
imFile下载管理器:从入门到精通的免费全能下载解决方案 【免费下载链接】imfile-desktop A full-featured download manager. 项目地址: https://gitcode.com/gh_mirrors/im/imfile-desktop imFile是一款功能全面的免费下载管理器,支持HTTP、FTP、…...
Linux小白避坑指南:Resilio Sync安装后权限配置与Web界面访问失败的常见问题解决
Linux权限迷宫:Resilio Sync安装后的深度避坑实战 当8888端口沉默时:一次真实的故障排查记录 上周五晚上11点,我正准备将团队的设计素材库同步到本地开发环境。按照官方文档,我在Ubuntu 22.04上顺利安装了Resilio Sync,…...
RAG视觉锚定:让大模型精准定位PDF中的图与表
1. 项目概述:让大模型真正“看见”文档里的图与表 “Visual Grounding for Advanced RAG Frameworks”——这个标题乍看像学术论文的副标题,但在我过去三年落地二十多个企业级RAG项目的过程中,它直指当前最棘手、也最容易被忽视的痛点&#x…...
芯片验证工程师的思维模式:从职业本能到生活与管理的利器
1. 从“找茬”到“共生”:一位芯片验证工程师的职业心路 “今天又抓了几个bug?” 这可能是我们验证工程师之间最常听到的问候语,其频率仅次于“咖啡机在哪”。十多年前,当我读到那篇关于“Bug是否侵扰了生活”的专栏时࿰…...
告别手动建造:TEdit免费地图编辑器如何10倍提升泰拉瑞亚创作效率
告别手动建造:TEdit免费地图编辑器如何10倍提升泰拉瑞亚创作效率 【免费下载链接】Terraria-Map-Editor TEdit - Terraria Map Editor - TEdit is a stand alone, open source map editor for Terraria. It lets you edit maps just like (almost) paint! It also l…...
别再死记公式了!用Python的NumPy和Matplotlib玩转坐标转换(附象限处理代码)
用Python实战坐标转换:从数学公式到可视化应用 坐标转换是计算机图形学、机器人学和数据可视化中的基础操作。传统教学中,我们往往被要求死记硬背转换公式,却很少有机会直观理解其实际应用场景。本文将带你用NumPy和Matplotlib这两个Python利…...
白起杀降将卒,项羽杀降,黄巢他们有的选择吗?
杀降不是暴君的个人意志,而是一场场被逼到极限的“系统自保”。 白起要为40万战俘找活路,项羽要喂活20万张嘴并防止后院起火,黄巢要让自己和十几万兄弟明天不饿死。杀降本身这份“答卷”固然是反人类的,但那份出题人的冷酷与无情&…...
Linux下Cursor AI编辑器自动化安装脚本设计与实现
1. 项目概述:为什么我们需要一个Cursor的Linux安装脚本如果你是一个在Linux环境下工作的开发者,并且对AI辅助编程工具感兴趣,那么Cursor这个名字你一定不陌生。作为一款集成了强大AI能力的代码编辑器,它正迅速成为许多程序员的新宠…...
告别臃肿!Dell G15笔记本散热控制的轻量级开源替代方案
告别臃肿!Dell G15笔记本散热控制的轻量级开源替代方案 【免费下载链接】tcc-g15 Thermal Control Center for Dell G15 - open source alternative to AWCC 项目地址: https://gitcode.com/gh_mirrors/tc/tcc-g15 你是否厌倦了Dell原厂AWCC软件的缓慢响应和…...
【LangChain】 入门:从分步调用到链式编程
LangChain 入门:从分步调用到链式编程本文基于一段翻译助手的示例代码,讲解 LangChain 的核心概念、输出解析器的作用,以及普通写法与链式写法的对比。一、LangChain 是什么? 名字拆解缩写含义LangLanguage(语言&#…...
