本文参考于CSDN博客wxy941011

1、疑问

我们使用第四个博客中的项目。

修改客户端为:连接成功后循环向服务器发送从1-100的数字。看看服务器会不会正常的接收100次数据。



可是我们发现服务器只接收了两次数据,为什么和期望的不一样呢,这就触发了粘包问题。

2、什么是粘包和拆包

当客户端不断向服务器发送数据包时,服务器就可能出现两个数据包粘在一起的情况。

而和Tcp同为传输层的Udp则不会发生粘包和拆包问题。因为Udp是基于报文发送的,从Udp帧结构可以看出,Udp首部采用了16bit来显示Udp数据报文长度,因此在应用层可以很好的把不同数据报文分开,避免了粘包和拆包问题。

TCP是基于字节流的,虽然应用层和TCP传输层之间的数据交互是大小不等的数据块,但是TCP把这些数据块仅仅看成一连串无结构的字节流,没有边界;另外从TCP的帧结构也可以看出,在TCP的首部没有表示数据长度的字段。所以只有Tcp会发生粘包、拆包现象

3、粘包拆包的表现形式

客户端向服务器连续发送两个数据包,packet1、packet2,服务器收到有三种情况。

  1. 正常收到了两个数据包

  2. 只收到一个数据包,由于Tcp不会出现丢包现象,所以这一个数据包包含了两个数据包的信息,称为粘包。

  3. 接收到两个错误的数据报,发生了粘包+拆包

4、粘包拆包的发生原因

  1. 要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包。
  2. 待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。
  3. 要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包。
  4. 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包。

    等等。

5、粘包拆包解决方法

1、发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。

2、发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。

3、可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开。

等等。

6、构造包头包尾方法

class EncodeTool
{
/// <summary>
/// 构造包 包头+包尾
/// </summary>
public static byte[] EncodePacket(byte[] data)
{
//内存流用完需要close释放,using自动释放。
using (MemoryStream ms = new MemoryStream())
{
//bw用于向ms流中写入内容
using (BinaryWriter bw = new BinaryWriter(ms))
{
//写入包头(数据的长度),把字节数组写入流
bw.Write(data.Length);
//写入包尾(数据)
bw.Write(data); //拿到写入的数据
byte[] packet = new byte[ms.Length];
Buffer.BlockCopy(ms.GetBuffer(), 0, packet, 0, (int) ms.Length); //把内存流中的数据复制到packet中
return packet;
}
}
} /// <summary>
/// 解析包,从缓冲区里取出一个完成的包
/// </summary>
public static byte[] DecodePacket(ref List<byte> cache)
{
if (cache.Count < 4)
{
return null;
} //这种构造实例根据byte类型的字节数组进行初始化
//并且实例的容量大小固定为字节数组的长度
using (MemoryStream ms = new MemoryStream(cache.ToArray()))
{
//从流中读取数据
using (BinaryReader br = new BinaryReader(ms))
{
int length = br.ReadInt32(); //读取前四个字节(包头-数据的长度)
//读取的长度和缓冲区剩余的长度进行比较
int remainLength = (int) ms.Length - (int) ms.Position; //流中剩余的长度
if (length > remainLength)
{
return null;
} byte[] data = br.ReadBytes(length);
//更新数据缓存
cache.Clear();
cache.AddRange(br.ReadBytes(remainLength)); //把剩余的再填进去???
return data;
}
}
} }

C#网络编程学习(5)---Tcp连接中出现的粘包、拆包问题的更多相关文章

  1. python 之网络编程(基于TCP协议Socket通信的粘包问题及解决)

    8.4 粘包问题 粘包问题发生的原因: 1.发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包),这样接收端,就难于分辨出来了,必须提供科学的拆包机制. ...

  2. 网络编程 -- RPC实现原理 -- Netty -- 迭代版本V4 -- 粘包拆包

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2——Netty -- new LengthFieldPrepender(2) : 设置数据包 2 字节的特征码 new LengthFieldB ...

  3. 网络编程学习笔记-TCP拥塞控制机制

    为了防止网络的拥塞现象,TCP提出了一系列的拥塞控制机制.最初由V. Jacobson在1988年的论文中提出的TCP的拥塞控制由“慢启动(Slow start)”和“拥塞避免(Congestion ...

  4. Java网络编程系列之TCP连接状态

    1.TCP连接状态 LISTEN:Server端打开一个socket进行监听,状态置为LISTEN SYN_SENT:Client端发送SYN请求给Server端,状态由CLOSED变为SYN_SEN ...

  5. 网络编程之模拟ssh远程执行命令、粘包问题 、解决粘包问题

    目录 模拟ssh远程执行命令 服务端 客户端 粘包问题 什么是粘包 TCP发送数据的四种情况 粘包的两种情况 解决粘包问题 struct模块 解决粘包问题 服务端 客户端 模拟ssh远程执行命令 服务 ...

  6. Linux 网络编程详解四(流协议与粘包)

    TCP/IP协议是一种流协议,流协议是字节流,只有开始和结束,包与包之间没有边界,所以容易产生粘包,但是不会丢包. UDP/IP协议是数据报,有边界,不存在粘包,但是可能丢包. 产生粘包问题的原因 . ...

  7. 网络编程基础【day09】:解决socket粘包之大数据(七)

    本节内容 概述 linux下运行效果 sleep解决粘包 服务端插入交互解决粘包问题 一.概述 刚刚我们在window的操作系统上,很完美的解决了,大数据量的数据传输出现的问题,但是在Linux环境下 ...

  8. Linux网络编程二、tcp连接API

    一.服务端 1.创建套接字: int socket(int domain, int type, int protocol); domain:指定协议族,通常选用AF_INET. type:指定sock ...

  9. 网络编程基础【day09】:socket解决粘包问题之MD5(八)

    本节内容 1.概述 2.代码实现 一.概述 上一篇博客讲到的用MD5来校验还是用的之前解决粘包的方法,就是客户端发送一个请求,等待服务端的确认的这样的一个笨方法.下面我们用另外一种方法:就是客户端已经 ...

随机推荐

  1. Python习题-登录

    写一个登录的程序,失败次数最多为3次,输入账号.密码错误,提示账号/密码错误.失败三次程序退出,输入正确,提示欢迎xxx登录 i=0while (i<3): username = input(' ...

  2. 解编码框架的比较(protobuf,thrift,Marshalling,xml)

    1.ProtoBuf 特点: 1.结构化数据存储格式 2.高效的解编码性能. 3.语言无关,平台无关,扩展性好. 4.官方支持java,c++,python三种语言. 5.性能比较好 (与之对比xml ...

  3. png8 png24 png32

    解释 PNG8 : 8位的PNG最多支持256(2的8次方)种颜色,8位的PNG支持不透明.索引透明.alpha透明. PNG24 : 支持2的24次方种颜色,表现为不透明.PS导出PNG24是会根据 ...

  4. PS 滤镜— —挤压效果

    clc; clear all; close all; addpath('E:\PhotoShop Algortihm\Image Processing\PS Algorithm'); I=imread ...

  5. 【遍历二叉树】08判断两个二叉树是否相同【Same Tree】

    迭代版本用的是二叉树的DFS,中的root->right->left +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ...

  6. 20179215《Linux内核原理与分析》第一周作业

    一.Linux介绍 我们现在很常见Windows系统,对于Linux则显得尤为陌生.当然我也不例外,初识Linux过程中遇到一些困惑,但我也在实验的同时通过不断查找资料与实践中慢慢解决问题.那么下面我 ...

  7. NYOJ-括号配对问题--------待解决,RE

    描述 现在,有一行括号序列,请你检查这行括号是否配对.   输入 第一行输入一个数N(0<N<=100),表示有N组测试数据.后面的N行输入多组输入数据,每组输入数据都是一个字符串S(S的 ...

  8. 使用MDI窗体实现多窗口效果

    本文章已收录于:   C#MDI窗体实现多窗口效果   Visual C#是微软公司推出的下一代主流程序开发语言,他也是一种功能十分强大的程 序设计语言,正在受到越来越多的编程人员的喜欢.在Visua ...

  9. 利用dynamic来提供动态方法的性能

    前段时间做了一个worklist的项目,有部分是利用xml配置DICOM的tag,然后根据xml把DICOM的Dataset转为实体类,或者把实体类转为Dataset. 当中主要应用了反射来调用Dat ...

  10. FPGA, Float 32bit, multiplyier by Verilog

    1, FPGA device, using three 18bit x 18 bit multiplier to implement 32bit float multiplier 2, compari ...