Socket粘包分包解析

一.粘包分包现象(来自Unity 3D网络实战一书)

粘包:
由于TCP协议本身的机制,客户端与服务器会维持一个连接发送数据,如果发送的网络数据包太小,TCP会等待,然后合并较小的数据包在发送,接收端便无法区分哪些数据是发送端自己分开的,因此便会产生粘包现象,或者接收端把数据放到tcp接受缓冲区中,如果数据没有及时从缓冲区取走,下次取数据时可能出现一次取出多个数据包的情况,如例,客户端发送两次数据,服务器一次接受:

客户端 Send: h e l l o

客户端 Send: u n i t y

服务器 Recv : h e l l o u n i t y

分包:
如果发送的数据包太大,TCP可能会把它拆分成多个包发送,接收端的一次Receive可能只收到一部分数据,如下例,如果发送的字符串较短,可能出现的概率很小,或者接收端的tcp接受缓冲区比较小时,每次读取,只能读取一部分.

客户端 Send:h e l l o u n i t y

服务器 Recv:h e l 

服务器 Recv: l o u n i t y

二.解决方案

处理粘包分包的一种方法是在每个数据包前面加上长度字节(数据包长度,每次接收到数据后放到应用层定义的缓冲区,然后先读取长度字节,如果缓冲区的数据长度大于要提取的字节数,就取出相应字节,否则等待下一次数据接受,如下,客户端要发送”hellounity“ 和”love“两个字符串,它在每个包前面加上一个代表字符串长度的字符,按照TCP机制,接收端收到的字节顺序一定和发送顺序一致(顺序不一致的话tcp在传输控制层会做处理,不会传给上层应用使用,在计算机网络自顶向下一书有说)。

1:假设第一次接收到的是"10hel",那么服务器程序将接受到的数据存入缓冲区,然后读取第一个字节”10“,此时缓冲区长度只有4,服务器不处理,等待下一次接受。

2.假设第二次接收到的是"lounity4l",此时缓冲区便有13个字节,超出第一个包所需要的11个字节(10个数据字节加一个长度字节。于是程序读取缓冲区前11个字节的数据并进行处理,之后缓冲区便只剩下”4l“两个字节。

3.假设第三次接收到的是"ove"三个字节,这时缓冲区便有了 ”4love“ 个5字节,程序读取缓冲区这5个字节并进行处理。

客户端 Send ①:10 h e l l o u n i t y

客户端 Send ②:4 l o v e

服务器 Buffer: 10 h e l

服务器 Buffer: 10 h e l l o u n i t y 4 l

服务器 Buffer: 4 l o v e


注意:上面都是假设所有字符按照1个字节来的,实际上包长度应该是一个int类型(4字节),数据按照协议的字符编码所占字节来计算的。

三.实际项目使用(如果有需求再详细查看文档以及和项目中demo)

1.一般都是定义通信协议,分为包头和包体,包头是固定长度

例如:包头: magicCode: 1byte 魔法值或版本号
pkeSize: 4byte 包体总长度
cmdId: 1byte 业务类型
包体: 用户业务自定义数据,数据长度为pkeSize


2. 那么java 程序员在写socket通信的话,该怎么办呢?

bio,nio这种原生的socket api,只能自己写逻辑,判断包长度够不够,不够就存到内存(或者缓存)中,等到下一次包到了,将上次的包拼到一起,如果是一个完整的包,继续走处理逻辑,所以其实解决粘包,分包其实很简单,但是切记
解决粘包:根据数据长度去读取
解决分包:数据包长度够,就用,不够就缓存起来,等着下次用

现在java大多都是使用netty框架实现socket通信,netty实现了很多解决粘包分包问题的解决方案(本质上就是我说的这种),降低用户在实际实现中的复杂度,详细的可以看书

udp没有粘包分包,因为底层使用的是链表结构(每次read都是一个完整的包),一次发送对应一次接受,tcp有,是因为tcp是流,没有边界的(可以理解为数组),所以需要协议去定义边界。

websocket没有粘包分包问题是因为websocket协议里解决了(数据包的第一个bit 表示是否是最后一个帧,然后还有数据长度等)
netty中websocket解决粘包,分包如下:
https://www.jianshu.com/p/30c26a755a87
https://blog.csdn.net/mafei6827/article/details/79886593

下面这篇文章关于tcp有一段写的很好
https://blog.csdn.net/weixin_32821913/article/details/113317948?spm=1001.2014.3001.5506

写本篇文章主要是给小白看的,因为很多人没接触过socket编程,对于粘包分包也没有概念,如果直接使用netty等框架,可能不是很好理解它里面封装的各种解码器。

看到文章末尾的,可以看下的我的程序人生这篇文章,主要是讲我在编程这条路上的经历,祝愿对你有用,感谢!

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值