本文参考于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基础-redis模块使用

    redis是一个数据库,他的数据全都是存放在内存里面的,redis每秒能支持30w次的读写,存放有两种格式,一种string类型,一种是hash类型 一,操作string类型 r=redis.Redi ...

  2. Java企业微信开发_05_消息推送之被动回复消息

    一.本节要点 1.消息的加解密 微信加解密包 下载地址:http://qydev.weixin.qq.com/java.zip      ,此包中封装好了AES加解密方法,直接调用方法即可. 其中,解 ...

  3. 【BZOJ 4199】[Noi2015]品酒大会 后缀自动机+DP

    题意 两个长度为$r$的子串相等称为$r$相似,两个$r$相似的权值等于子串开头位置权值乘积,给定字符串和每个位置权值,求$r$相似子串数量和最大权值乘积 对反串建立后缀自动机得到后缀树,后缀树上两个 ...

  4. 在开发环境中,自己搭建一个ssl环境(小例子)

    做项目的时候自己总结的一些小例子 public class Test { public static void setSSLProperty() { Security.addProvider(new  ...

  5. 移植memtester到android平台

    硬件搭建起来能进入系统,首要就是测试内存的稳定性,需要一款内存测试工具. 一般都是选择memtester这款linux软件,下载地址如下:http://pyropus.ca/software/memt ...

  6. PRVF-0002 : could not retrieve local node name

    安装 oracle 的时候,./runInstaller 启动报错  PRVF-0002 : could not retrieve local node name 碰到这个错误是因为 OUT试图对你主 ...

  7. 非系统数据文件损坏,rman备份恢复

    实验前提:已经做好备份. SQL> col file_name for a50select file_id,file_name from dba_data_files; FILE_ID FILE ...

  8. 1. Two Sum[LeetCode 简单 by 大志]

    1. 二数之和 题目 English Given an array of integers, return indices of the two numbers such that they add ...

  9. Poj 2533 Longest Ordered Subsequence(LIS)

    一.Description A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequenc ...

  10. QTP连接数据库

    '注意:其中DSN=数据源名:UID=用户名:PWD=用户密码 Dim Conn Set Conn=CreateObject("ADODB.Connection") Const C ...