一:TCP粘包产生的原理
1,TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。

2,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据。接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。

3,这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。
三:解决原理及代码实现
1,采用包头(固定长度,里面存着包体的长度,发送时动态获取)+包体的传输机制。如图

HeaderSize 存放着包体的长度,其HeaderSize本身是定长4字节;
一个完整的数据包(L)=HeaderSize+BodySize;、
2,分包算法

其基本思路是首先将待处理的接收数据流即系统缓冲区数据(长度设为M)强行转换成预定的结构数据形式,并从中取出结构数据长度字段L,而后根据包头计算得到第一包数据长度。

   M=系统缓冲区大小;L=用户发送的数据包=HeaderSize+BodySize;发送数据的时候,先构造固定长度的表头数据(比如4个字节)+需要发送的数据

///上述内容引用https://www.cnblogs.com/wangjun8868/p/7160661.html

  class Program{static void Main(string[] args){///客户端代码Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("192.168.1.102"), 3344);clientSocket.Connect(ipEndPoint);byte[] receiveBuffer = new byte[1024];int count = clientSocket.Receive(receiveBuffer);string msg = Encoding.UTF8.GetString(receiveBuffer, 0, count);Console.WriteLine("接收到服务端的消息:" + msg);for (int i = 0; i < 100; i++) ///客户端启动向服务端发送250条数据{clientSocket.Send(SendMsg(i.ToString()));}Console.ReadKey();}/// <summary>/// 构造发送数据/// </summary>/// <param name="msg"></param>/// <returns></returns>public static byte[] SendMsg(string msg){int length = msg.Length;//构造表头数据,固定4个字节的长度,表示内容的长度byte[] headerBytes = BitConverter.GetBytes(length);//构造内容byte[] bodyBytes = Encoding.UTF8.GetBytes(msg);byte[] tempBytes = new byte[headerBytes.Length + bodyBytes.Length];///拷贝到同一个byte[]数组中,发送出去..Buffer.BlockCopy(headerBytes, 0, tempBytes, 0, headerBytes.Length);Buffer.BlockCopy(bodyBytes, 0, tempBytes, headerBytes.Length, bodyBytes.Length);return tempBytes;}}

/服务端代码

    class Program{static void Main(string[] args){///服务端代码Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress ip = IPAddress.Parse("192.168.1.102");IPEndPoint ipEndPoint = new IPEndPoint(ip, 3344);serverSocket.Bind(ipEndPoint);serverSocket.Listen(0);//开启监听Console.WriteLine("服务器启动");//开始异步接收客户端serverSocket.BeginAccept(AcceptAsyncCallBack, serverSocket);Console.ReadKey();}/// <summary>/// 异步等待客户端回调方法/// </summary>/// <param name="ar"></param>private static void AcceptAsyncCallBack(IAsyncResult ar){Socket serverSocket = ar.AsyncState as Socket;//传递过来的参数Socket clientSokcet = serverSocket.EndAccept(ar); //一个客户端连接过来了string msg = ":Hello client! 你好......";byte[] dataBytes = Encoding.UTF8.GetBytes(msg);  //网络连接收发数据,只能发送byte[] 字节数组clientSokcet.Send(dataBytes);///messageHandle.DataBuffer缓存区,messageHandle.ContentSize(缓存区中已经存在的内容长度开始存)///  messageHandle.remainSize 缓存区中剩余可以存储的空间clientSokcet.BeginReceive(messageHandle.DataBuffer, messageHandle.ContentSize, messageHandle.remainSize, SocketFlags.None, ReceiveCallBack, clientSokcet); //开始异步接收数据serverSocket.BeginAccept(AcceptAsyncCallBack, serverSocket);//循环等待客户端接收....}static MessageHandle messageHandle = new MessageHandle();/// <summary>/// 异步接收数据回调方法/// </summary>/// <param name="ar"></param>private static void ReceiveCallBack(IAsyncResult ar){Socket clientSocket = null;try{clientSocket = ar.AsyncState as Socket;int count = clientSocket.EndReceive(ar);    //接收到的数据量if (count == 0) ///说明客户端已经已经断开连接了{if (clientSocket != null){clientSocket.Close();}return;}//j解析数据(把新接收的数据传入)messageHandle.ReadMessage(count);//开始异步接收数据clientSocket.BeginReceive(messageHandle.DataBuffer, messageHandle.ContentSize, messageHandle.remainSize, SocketFlags.None, ReceiveCallBack, clientSocket); }catch (Exception e)///说明客户端已经已经断开连接了,异常断开{Console.WriteLine(e);if (clientSocket != null){clientSocket.Close();}}}}
服务午安数据发送,或者接收数据解析类
  public class MessageHandle{//表头的数据长度为4个个字节,表示后面的数据的长度//保证能够每次接收发送的消息小于1024bit大小,否则无法完整接收整条数据private byte[] dataBuffer = new byte[1024];//从dataBuffer已经存了多少个字节数据private int contentSize = 0;public int ContentSize {get { return contentSize; }}/// <summary>/// 剩余多少存储空间/// </summary>public int remainSize {get { return dataBuffer.Length - contentSize; }}public byte[] DataBuffer {get { return dataBuffer; }}/// <summary>/// 解析数据 ,count 新读取到的数据长度/// </summary>public void ReadMessage(int count){contentSize += count;//用while表示缓存区,可能有多条数据while (true){//缓存区小于4个字节,表示连表头都无法解析if (contentSize <= 4) return;//读取四个字节数据,代表这条数据的内容长度(不包括表头的4个数据)int receiveCount = BitConverter.ToInt32(dataBuffer, 0);//缓存区中的数据,不够解析一条完整的数据if (contentSize - 4 < receiveCount) return;//2、解析数据//从除去表头4个字节开始解析内容,解析的数据长度为(表头数据表示的长度)string receiveStr = Encoding.UTF8.GetString(dataBuffer, 4, receiveCount);Console.WriteLine("接收的客户端数据:" + receiveStr);//把剩余的数据Copy到缓存区头部位置Array.Copy(dataBuffer, 4 + receiveCount, dataBuffer, 0, contentSize - 4 - receiveCount);contentSize = contentSize - 4 - receiveCount;}}/// <summary>/// 构造发送数据/// </summary>/// <param name="msg"></param>/// <returns></returns>public byte[] SendMsg(string msg){int length = msg.Length;//构造表头数据,固定4个字节的长度,表示内容的长度byte[] headerBytes = BitConverter.GetBytes(length);//构造内容byte[] bodyBytes = Encoding.UTF8.GetBytes(msg);byte[] tempBytes = new byte[headerBytes.Length + bodyBytes.Length];///拷贝到同一个byte[]数组中,发送出去..Buffer.BlockCopy(headerBytes, 0, tempBytes, 0, headerBytes.Length);Buffer.BlockCopy(bodyBytes, 0, tempBytes, headerBytes.Length, bodyBytes.Length);return tempBytes;}}
for循环中向服务端发送100条数据实验,服务端正确解析100条数据

for循环中向服务端发送1000条数据实验,服务端正确解析1000条数据

C# TCP网络编程5(分包和粘包二)相关推荐

  1. (Java)socket网络编程及处理socket粘包拆包问题

    目录 1.socket简介 2.TCP/IP协议 3.tcp三次握手 4.socket的一些接口函数原理 5.java socket 长连接粘包拆包问题 6.socket模拟服务端客户端发消息 7.U ...

  2. 【NIO与Netty】网络编程:netty中粘包、半包现象展示,分析及解决

    一.粘包现象 服务端 public static void main(String[] args) {NioEventLoopGroup bossGroup=new NioEventLoopGroup ...

  3. muduo学习笔记:net部分之实现TCP网络编程库-Buffer

    文章目录 为什么采用non-blocking网络编程中应用层buffer是必需的? Buffer 设计 Buffer::readFd() 线程安全 Muduo Buffer 的数据结构 Muduo B ...

  4. UDP和TCP网络编程

    UDP和TCP 套接字(socket): 实现网络编程进行数据传输的一种技术手段,网络上的各种网络服务大多都是基于socket来完成通信的. python套接字编程模块:import socket U ...

  5. 迈入JavaWeb第一步,Java网络编程基础,TCP网络编程URL网络编程等

    文章目录 网络编程概述 网络通信要素 要素一IP和端口号 要素二网络协议 TCP网络编程 UDP网络编程 URL网络编程 Java网络编程基础 网络编程概述 Java是Internet上的语言,它从语 ...

  6. TCP网络编程的基本流程

    TCP网络编程的基本流程 对于服务端,通常为以下流程: 调用socket函数创建socket 调用bind函数将socket绑定到某个IP和端口上 调用listen开始监听 当有客户端请求连接上来时, ...

  7. TCP/IP网络编程之多进程服务端(二)

    TCP/IP网络编程之多进程服务端(二) 信号处理 本章接上一章TCP/IP网络编程之多进程服务端(一),在上一章中,我们介绍了进程的创建和销毁,以及如何销毁僵尸进程.前面我们讲过,waitpid是非 ...

  8. 【计算机网络】Linux环境中的TCP网络编程

    文章目录 前言 一.TCP Socket API 1. socket 2. bind 3. listen 4. accept 5. connect 二.封装TCPSocket 三.服务端的实现 1. ...

  9. TCP网络编程 [Java]

    TCP网络编程 这里我们通过一个例子来了解什么是TCP网络编程: eg: 问题: -> 从客户端发送文件到服务端,服务端将接受到的客户端发来的文件存储到本地之后并返回一个发送成功给客户端,并关闭 ...

最新文章

  1. 【C语言运算符大全】快速学会C语言运算符
  2. 必须为非自相关端口上的非激活接收指定至少一个已初始化的相关集
  3. 多个线程访问统一对象的不同方法_不会多线程还想进BAT?精选19道多线程面试题,有答案边看边学...
  4. 移位操作提高代码的可读性_本地记录或类,以提高流操作的可读性
  5. java mask_Java 三大属性:
  6. 扩展中国剩余定理 exCRT 学习笔记
  7. 完整的 .NET Core 目标框架的预处理器符号列表
  8. 怎么提前体验鸿蒙,鸿蒙OS手机版明天发布,开发者提前体验,上手操作毫无难度...
  9. Javascript FormData实例
  10. 我的chrome插件
  11. 如果命运是一条孤独的河流,谁会是你的灵魂摆渡人
  12. [unity3d插件]插件效果以及下载
  13. 千方百剂2008升级到千方百计II 脚本执行错误 请检查第69行
  14. 软工网络15团队作业8——Beta阶段冲刺合集
  15. 16代表啥_16代表的爱情恋爱含义16代表什么爱情含义
  16. Ubuntu 机箱前置耳机没声音的问题(彻底解决)
  17. 波士顿学院的计算机科学,权威公布:美国最强商学院,TOP5里有你的梦校吗?...
  18. message: 没有找到可以构建的 NPM 包,请确认需要参与构建的 npm 都在 `miniprogramRoot` 目录内,或配置 project.config.json 的 packNpmMa
  19. 怎么样可以把pdf合并在一起呢
  20. matlab矩阵除法、左除、求逆,这三者有什么区别

热门文章

  1. python学习笔记1.4 对象和模块
  2. Webots模拟发射射频红外串行信号
  3. CorelDRAW——VBA_2-2常量
  4. js实现分数计算器的代码
  5. jeeplus代码中配置并开发流程
  6. 我如何选择Parse.com的替代品第2部分
  7. 苹果9月会发布什么?除了三款iPhone还有一大堆产品
  8. setHeader的用法大全
  9. 【报告分享】2021中国互联网医疗内容行业研究报告-亿欧智库(附下载)
  10. cmi码型变换matlab程序_CMI码形变换实验完整实验报告.doc