首页 > 其他分享 >黏包现象

黏包现象

时间:2022-11-17 16:15:55浏览次数:50  
标签:struct 黏包 len 现象 dict 长度 报头 data

黏包现象

我们先来看一个案例:

server端:

server = socket.socket()
server.bind(('127.0.0.1', 8081))
server.listen(5)

sock, addr = server.accept()
data1 = sock.recv(1024)
print(data1)
data2 = sock.recv(1024)
print(data2)
data3 = sock.recv(1024)
print(data3)

sock.close()
server.close()

client端:

import socket

client = socket.socket()
client.connect(('127.0.0.1', 8081))

client.send(b'hello kevin')
client.send(b'jason say hei')
client.send(b'jerry say goodbye')

服务端收到的结果如下:

上述现象称之为黏包现象:

  1.服务端连续执行三次recv
  2.客户端连续执行三次send

  结果:服务端一次性接收到了客户端三次的消息,该现象称为"黏包现象"。

黏包现象产生的原因:

1.接收方不知道消息之间的界限,不知道一次性提取多少字节的数据
2.TCP也称为流式协议:数据像水流一样绵绵不绝没有间隔(TCP会针对数据量较小且发送间隔较短的多条数据一次性合并打包发送)

黏包的解决方案

 避免黏包现象的核心思路\关键点:
       如何明确即将接收的数据具体有多大

  如何将长度变化的数据全部制作成固定长度的数据?

接下来我们介绍一个模块:

struct模块

该模块可以把一个类型,如数字,转成固定长度的bytes

struct.pack('i',1111111111111)

struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #这个是范围

 

struct模块作用:将数据打包成固定的长度

import struct

info = b'hello big baby'
print(len(info))  # 数据真实的长度(bytes)  14
res = struct.pack('i', len(info))  # 将数据打包成固定的长度 i是固定的打包模式
print(len(res))  # 打包之后长度为(bytes)   4           报头

real_len = struct.unpack('i', res)
print(real_len)  # (14,)             根据固定长度的报头 解析出真实数据的长度


desc = b'hello my baby I will take you to play big ball' print(len(desc)) # 数据真实的长度(bytes) 46 res1 = struct.pack('i', len(desc)) print(len(res1)) # 打包之后长度为(bytes) 4 报头 real_len1 = struct.unpack('i', res1) print(real_len1) # (46,) 根据固定长度的报头 解析出真实数据的长度

解决黏包问题初级版本

客户端
  1. 将真实数据转成bytes类型并计算长度
  2. 利用struct模块将真实长度制作一个固定长度的报头
  3. 将固定长度的报头先发送给服务端,服务端只需要在recv括号内填写固定长度的报头数字即可
  4. 然后再发送真实数据

服务端
  1. 服务端先接收固定长度的报头
  2. 利用struct模块反向解析出真实数据长度
  3. recv接收真实数据长度即可

但是上述方案也存在一些问题:

问题1:struct模块无法打包数据量较大的数据,就算换更大的模式也不行

res = struct.pack('i', 12313213123)
print(res)    # struct.error: argument out of range

问题2:报头能否传递更多的信息,比如要传递一个电影,电影大小、电影名称、电影评价、电影简介

终极解决方案:字典作为报头打包:效果更好,数字更小

  客户端
    1. 制作真实数据的信息字典(数据长度、数据简介、数据名称)
    2. 利用struct模块制作字典的报头
    3. 发送固定长度的报头(解析出来是字典的长度)
    4. 发送字典数据
    5. 发送真实数据
  服务端
    1.接收固定长度的字典报头
    2.解析出字典的长度并接收
    3.通过字典获取到真实数据的各项信息
    4.接收真实数据长度

黏包问题代码实操

client端:

import socket
import os
import struct
import json

client = socket.socket()
client.connect(('127.0.0.1', 8081))

'''任何文件都是下列思路 图片 视频 文本 ...'''
# 1.获取真实数据大小
file_size = os.path.getsize(r'/Users/jiboyuan/PycharmProjects/day36/xx老师合集.txt')
# 2.制作真实数据的字典数据
data_dict = {
    'file_name': '有你好看.txt',
    'file_size': file_size,
    'file_desc': '内容很长 准备好吃喝 我觉得营养快线挺好喝',
    'file_info': '这是我的私人珍藏'
}
# 3.制作字典报头
data_dict_bytes = json.dumps(data_dict).encode('utf8')
data_dict_len = struct.pack('i', len(data_dict_bytes))
# 4.发送字典报头
client.send(data_dict_len)  # 报头本身也是bytes类型 我们在看的时候用len长度是4
# 5.发送字典
client.send(data_dict_bytes)
# 6.最后发送真实数据
with open(r'/Users/jiboyuan/PycharmProjects/day36/xx老师合集.txt', 'rb') as f:
    for line in f:  # 一行行发送 和直接一起发效果一样 因为TCP流式协议的特性
        client.send(line)
import time
time.sleep(10)

server端:

import socket
import struct
import json


server = socket.socket()
server.bind(('127.0.0.1', 8081))
server.listen(5)

sock, addr = server.accept()
# 1.接收固定长度的字典报头
data_dict_head = sock.recv(4)
# 2.根据报头解析出字典数据的长度
data_dict_len = struct.unpack('i', data_dict_head)[0]
# 3.接收字典数据
data_dict_bytes = sock.recv(data_dict_len)
data_dict = json.loads(data_dict_bytes)  # 自动解码再反序列化
# 4.获取真实数据的各项信息
total_size = data_dict.get('file_size')
with open(data_dict.get('file_name'), 'wb') as f:
    f.write(sock.recv(total_size))
接收真实数据的时候,如果数据量非常大,recv括号内直接填写该数据量,不太合适,我们可以每次接收一点点,反正知道总长度
total_size = data_dict.get('file_size')
recv_size = 0
with open(data_dict.get('file_name'), 'wb') as f:
    while recv_size < total_size:
        data = sock.recv(1024)
        f.write(data)
        recv_size += len(data)
        print(recv_size)

 

标签:struct,黏包,len,现象,dict,长度,报头,data
From: https://www.cnblogs.com/chen-ao666/p/16899767.html

相关文章

  • 黏包现象及解决办法
    黏包现象1.服务端连续执行三次recv#服务端收3次2.客户端连续执行三次send#客户端发3次问题:服务端一次性接收到了客户端3次的消息,该现象称为'黏包现象'黏包现......
  • 排查http接口自动重试现象
    http接口自动重试现象的排查标题现象排查过程结论原因一是公司kong的配置:原因二:整体解释现象在公司开发时观察到一个奇怪的现象,一个运行时间较长(1分钟)的http接口会被自动......
  • OSI七层协议之传输层,应用层,socket模块简介,socket模块基本使用,黏包问题
    目录OSI七层协议之传输层,应用层,socket模块简介,socket模块基本使用,黏包问题今日内容概要今日内容详细传输层之TCP与UDP协议应用层socket模块socket代码简介代码优化半连接池......
  • 030_八锁现象
    目录synchronized对象锁第一种情况第二种情况第三种情况第四种情况synchronized类锁第五种情况第六种情况第七种情况第八种情况synchronized对象锁第一种情况:::in......
  • 再用国产操作系统deepin出现拖影现象
    问题如题,使用deepin系统后发现不论是网页的拖动、滑动都会出现明显拖影现象,最神奇的是使用爱奇艺的客户端播放器时同样出现拖影现象。  不过这个拖影现象截图还体现不......
  • H.265流媒体播放器EasyPlayer手机端播放画面出现强制拉伸现象的解决办法
    我们在前期的文章中介绍过关于H5网页播放器EasyPlayer的功能更新,包括已经实现网页端实时录像、在iOS上实现低延时直播等。EasyPlayer流媒体播放器性能稳定、播放流畅,属于高......
  • H.265流媒体播放器EasyPlayer手机端播放画面出现强制拉伸现象的解决办法
    我们在前期的文章中介绍过关于H5网页播放器EasyPlayer的功能更新,包括已经实现网页端实时录像、在iOS上实现低延时直播等。EasyPlayer流媒体播放器性能稳定、播放流畅,属于高......
  • Netty进阶-黏包半包分析
    Netty进阶1、黏包服务端//测试黏包服务端@Slf4jpublicclassTestNianbaoServer{publicstaticvoidmain(String[]args){NioEventLoopGroupboss=......
  • Netty进阶-黏包半包解决方案
    4、解决方案4.1、短连接以解决黏包为例。服务端同上,客户端如下//短连接处理黏包,发送后关闭publicclassTestSloveNianbaoClient{publicstaticvoidmain(Strin......
  • FXGL游戏开发-物理现象
    在实现生活中物理现象是客观存在的,FXGL游戏框架也提供了相应的组件,那就是PhysicsComponent。利用PhysicsComponent可以实现现实世界中的物理现象,比如:自由落体,看看使......