本文参考于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. JavaUtil_00_资源帖

    一.精选工具包 1.Hutool Hutool wiki 二.资源 1.

  2. 我对java的理解(二)——反射是小偷的万能钥匙

    在我们生活中,车上或者路上有时候会遇到一种很讨厌的人——“小偷”,趁我们不注意或者疏忽的时候拿走属于我们的东西.更有甚者,趁我们不在家的时候,手持一把万能钥匙,打开我们的房门,悠闲的查看房间的布置,翻 ...

  3. hdu-5861 Road(并查集)

    题目链接: Road Time Limit: 12000/6000 MS (Java/Others)     Memory Limit: 65536/65536 K (Java/Others) Pro ...

  4. HTML5视音频标签参考

    本文将介绍HTML5中的视音频标签和对应的DOM对象.是相关资料的中文化版本,可以作为编写相关应用的简易中文参考手册. 一些约定 所有浏览器:指支持HTML5的常见桌面浏览器,包括IE9+.Firef ...

  5. FEC之异或运算应用

    话说为啥FEC需要异或( ^/⊕ )操作呢? 异或:xor 异或运算规则: 0 xor 0 = 0 0 xor 1 = 1 1 xor 0 = 1 1 xor 1 = 0 异或运算特性: 1). a ...

  6. bzoj 4500: 矩阵 差分约束系统

    题目: Description 有一个n*m的矩阵,初始每个格子的权值都为0,可以对矩阵执行两种操作: 选择一行, 该行每个格子的权值加1或减1. 选择一列, 该列每个格子的权值加1或减1. 现在有K ...

  7. Linux 系统通过 Squid 配置实现代理上网

    本文转载自:https://help.aliyun.com/knowledge_detail/41342.html Squid 介绍 Squid 是一个缓存 Internet 数据的软件,其接收用户的 ...

  8. boost::io_service解读

    boost::io_service解读 asio是boost提供的一个c++异步编程模型库,其核心类io_service,在多线程编程里面提供了任务队列和任务分发功能,在socket.io编程里主要作 ...

  9. js之翻牌游戏中的一个深刻感悟

    先“上菜”: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...

  10. HBase 官方文档

    HBase 官方文档 Copyright © 2010 Apache Software Foundation, 盛大游戏-数据仓库团队-颜开(译) Revision History Revision ...