本文参考于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. Oracle_Exception_01_The Network Adapter could not establish the connection

    1.IP错误或端口错误. 在设置URL时错误,例如 url="jdbc:oracle:thin:@192.168.1.11:1521:mas" 数据库服务器不正确:ping 服务器 ...

  2. POJ2891Strange Way to Express Integers (线性同余方程组)

    Elina is reading a book written by Rujia Liu, which introduces a strange way to express non-negative ...

  3. Cannot resolve class or package 'springframework' less... (Ctrl+F1) Inspection info:Spring XML mode

    其实这个问题是由于MySQL 这个jar 包依赖类型默认是runtime ,也就是说只有运行时生效,所以虽然这里报错,但是不影响你代码运行. 解决方案: 将runtime 修改为Compile 即可 ...

  4. POJ2069:Super Star

    我对模拟退火的理解:https://www.cnblogs.com/AKMer/p/9580982.html 我对爬山的理解:https://www.cnblogs.com/AKMer/p/95552 ...

  5. MySQL读取各个my.cnf配置文件的先后顺序是:

    /etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf ~/.my.cnf 其他自定义路径下的my.cnf,例如:/data/mysql/y ...

  6. JavaScript编写代码时候没有提示

    如上图所示如果没有提示可能是箭头所指示的类型不正确.

  7. poj 2105 IP Address(水题)

    一.Description Suppose you are reading byte streams from any device, representing IP addresses. Your ...

  8. Azure PIP (Instance Level Public IP)

    微软的Azure平台已经支持Instance Level Public IP功能.当有复杂协议的情况下,需要开启多个端口的情况下,可以考虑开启PIP功能. 先介绍几个概念: VIP – virtual ...

  9. 【转】Ruby on Rails中select使用方法

    在Ruby on Rails中真的有一堆Select helper可以用,我们经常容易混淆.常见的有三个..select, select_tag, collection_select(其余的什么sel ...

  10. JavaScript设计模式--桥梁模式--引入

    1.使用情况 (1)事件的监控 #1,利用页面的button来选择宠物的例子(思路) button.addEvent(element,"click",getPetByBame); ...