首页 > 其他分享 >黏包

黏包

时间:2022-11-17 19:22:49浏览次数:53  
标签:struct 黏包 len dict 报头 data size

黏包现象

1.何为黏包现象
    黏包现象就是客户端连续多次执行send,服务端连续执行相同次数的recv想对应接收客户端发送过来的数据,但是服务端却一次性接受了客户端发来的数据,这就是黏包现象
2.黏包现象产生的原因
    1.每次发送的数据大小未知
    2.TCP流式协议,就是TCP会针对数据量较小且发送间隔较短的多条数据进行合并一次性打包发送
3.解决黏包问题的方法
    要知道每次发送的数据大小,并告诉服务端

struct模块

可以将数据打包成固定的长度,基于struct模块我们可以解决黏包问题
解决黏包问题流程
    客户端
        1.先将真实数据转成bytes类型并计算长度
        2.使用struct模块制作一个固定长度的报头
        3.将制作的固定长度报头发送给服务器
        4.最后再发送真实数据
    服务端
        1.先接受固定长度的报头
        2.使用struct模块反向解析出真实数据长度
        3.接收真实数据
        
代码
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,)              根据固定长度的报头 解析出真实数据的长度

这样做确实可以解决黏包问题,但是有缺陷,因为struct模块无法打包数据量较大的数据,更换模式也不行、
struct.pack('i',1111111111111)
struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #这个是范围
这个时候我们可以使用字典作为报头打包(这也是黏包问题的终极解决方案)
data_dict = {
    'file_name': 'xxx.mp4',
    'file_size': 123132131232342342423423423423432423432,
    'file_info': '内容很精彩 千万不要错过',
    'file_desc': '一代神作 私人珍藏'
}
import json
data_json = json.dumps(data_dict)
print(len(data_json.encode('utf8')))  # 真实字典的长度  228
res = struct.pack('i', len(data_json.encode('utf8')))
print(len(res))
这个时候的流程是
客户端 
        1.制作真实数据的信息字典(数据长度、数据简介、数据名称)
        2.利用struct模块制作字典的报头
        3.发送固定长度的报头(解析出来是字典的长度)
        4.发送字典数据
        5.发送真实数据     
    服务端
        1.接收固定长度的字典报头
        2.解析出字典的长度并接收
        3.通过字典获取到真实数据的各项信息
        4.接收真实数据长度

黏包代码实战

服务端
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)


客户端
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)

标签:struct,黏包,len,dict,报头,data,size
From: https://www.cnblogs.com/zyg111/p/16900510.html

相关文章

  • 黏包 struct模块 进程理论进程的并行与并发
    今日内容黏包现象1.服务端连续三次执行recv2.客户端连续三次执行send问题:服务端一次性接收到了客户端三次消息该现象称为"黏包现象"黏包现象产生的原因 1.不知道......
  • 黏包现象 并发编程
    目录黏包现象struct模块struct.pack()struct.unpack()文件过大无法打包黏包问题解决黏包实战UDP协议(了解)操作系统发展史穿孔卡片阶段联机批处理系统脱机批处理系统并发......
  • 黏包、udp协议、多道技术、进程并行与并发
    目录黏包现象struct模块黏包代码实战UDP协议(了解)并发编程理论多道技术进程理论进程的并行与并发进程的三状态黏包现象1.服务端连续执行三次recv2.客户端连续执行三次sen......
  • 黏包现象
    黏包现象我们先来看一个案例:server端:server=socket.socket()server.bind(('127.0.0.1',8081))server.listen(5)sock,addr=server.accept()data1=sock.rec......
  • 黏包现象及解决办法
    黏包现象1.服务端连续执行三次recv#服务端收3次2.客户端连续执行三次send#客户端发3次问题:服务端一次性接收到了客户端3次的消息,该现象称为'黏包现象'黏包现......
  • OSI七层协议之传输层,应用层,socket模块简介,socket模块基本使用,黏包问题
    目录OSI七层协议之传输层,应用层,socket模块简介,socket模块基本使用,黏包问题今日内容概要今日内容详细传输层之TCP与UDP协议应用层socket模块socket代码简介代码优化半连接池......
  • Netty进阶-黏包半包分析
    Netty进阶1、黏包服务端//测试黏包服务端@Slf4jpublicclassTestNianbaoServer{publicstaticvoidmain(String[]args){NioEventLoopGroupboss=......
  • Netty进阶-黏包半包解决方案
    4、解决方案4.1、短连接以解决黏包为例。服务端同上,客户端如下//短连接处理黏包,发送后关闭publicclassTestSloveNianbaoClient{publicstaticvoidmain(Strin......