网络编程 套接字socket 及 粘包 (2)

粘包现象的根本原因 : 系统缓冲区

\'\'\' 服务端 发出 客户端 接收 第一次dir 数据418 < 1024 数据全部发出 数据全部接收 第二次ipconifg 数据1517 > 1024 数据只发送1024个字节 数据只接收1024个字节 第三次 dir 数据418 < 1024 数据是谁? 数据接收493个字节 \'\'\' socket中造成粘包的原因

基于tcp协议的socket

recv时客户端一次接收不完, 下次继续接收(如果间隔时间过长,后续的数据会与之前剩余的数据黏在一起)

send数据时,连续的发送少量的数据(时间间隔很短),这些数据会积压在一起发送出去

发生粘包的原因主要是tcp是流式传输 , 无法判定不同数据的边界 ,传送到输入或输出缓冲区后发生积压现象

有两种情况
第一种是从输入缓冲区抓取数据时,无法将缓冲区的数据全部取出,导致下一次的数据传入缓冲区连接在一起
第二种是连续发送短数据,使输出缓冲区中的数据被积压,最后一起被发送

系统缓冲区 缓冲区的定义

缓冲区是内存中的一块区域

缓冲区的作用

如果你的网络出现短暂的异常或者波动接收数据就会出现短暂的中断,影响你下载或者上传的效率

缓冲高速设备与低速设备之间的速度差异,就socket的缓冲区而言,缓冲区的作用就是释放cpu,使cpu不用时刻接收发来的数据,cpu只需要在缓冲区中有数据时才去接受数据

但是 凡事都是双刃剑 , 缓冲区解决了上传下载的传输效率的问题 , 同时带来了粘包现象

提高上传下载的效率 , 保持稳定性 , 释放cpu ,减少与磁盘的交互

粘包的解决方案

错误示例

扩大recv的上限. recv(10240000000000) / 不是解决这个问题的根本原因 , 缓冲内容一直占内存中

故意延长recv的时间 , sleep 这样会非常影响效率

思路

分析一下功能

send的多次, recv一次 (不是一发一收制)

解决粘包现象的思路分析

当我第二次给服务器发送命令之前 , 我应该 循环 recv 直至将所有的数据全部取完

问题 :

如何限制循环次数?

当你发送的总bytes个数,与接收的总bytes个数相等时 , 循环结束

如何获取发送的总bytes 个数 : len() ---->3400字节 int

所以 :

服务端

send(总个数)

send(总数据)

总个数是什么类型? int() 3400 , send 需要发送bytes类型

send(总个数)

​ 将int 转外成 bytes 即可 b\' 3400

​ 方案一:

str(3400) --->\'\'3400"---->b\'3400

解决统一头部问题

无论总字节个数是多少?

将不固定的int类型, 转化成固定长度的bytes类型 , 方便获取头部信息

用len获取bytes的总字节数 s1 = "alexsb" b1 = s1.encode("utf-8") print(b1) print(len(b1)) # 6 len 可以计算 utf-8 的 字节数 import struct import struct # 将一个数字转化成等长度的bytes类型。 ret = struct.pack(\'i\', 183346) print(ret, type(ret), len(ret)) # b\'2\xcc\x02\x00\' <class \'bytes\'> 4 # 通过unpack反解回来 ret1 = struct.unpack(\'i\',ret) print(ret1,type(ret), len(ret)) # (183346,) <class \'bytes\'> 4 print(ret1[0]) # 但是通过struct 处理不能处理太大 # ret2 = struct.pack(\'i\', 1234567890) # 极限长度 再长就报错 print(ret2, type(ret2), len(ret2)) # b\'\xd2\x02\x96I\' <class \'bytes\'> 4 # 报错长度 ret2 = struct.pack(\'i\', 12345678900) # 极限长度 再长就报错 print(ret2, type(ret2), len(ret2)) # 报错

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwzfys.html