首页 > 编程语言 >网络编程 黏包现象

网络编程 黏包现象

时间:2022-11-17 20:11:38浏览次数:47  
标签:socket 黏包 编程 网络 len server dict 报头 data

黏包问题

1.服务端连续执行三次recv
2.客户端连续执行三次send
"""服务端一次性接收到了客户端三次的消息 该现象称为黏包现象"""

服务端:
import socket

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

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

客户端:
import socket

client = socket.socket()
client.connect(('127.0.0.1', 8082))
client.send(b'jason')
client.send(b'jerry')
client.send(b'kevin')

结果:jasonjerrykevin

黏包现象产生的原因
    1.不知道每次的数据到底多大
    2.TCP也称为流式协议:数据像水流一样绵绵不绝没有间隔(TCP会针对数据量较小且发送间隔较短的多条数据一次性合并打包发送)

避免黏包现象的核心:明确即将接收的数据具体有多大

struct模块

import struct

info = b'hello big baby'
print(len(info))        # 数据的真实长度   14

res = struct.pack('i',len(info))   # 将数据打包成固定的长度,i是固定的打包模式
print(len(res))         # 打包之后的长度   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))        # 数据的真实长度  46

res1 = struct.pack('i',len(desc))
print(len(res1))        # 打包后的长度  4

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

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

"""问题1:struct模块无法打包数据量较大的数据就算换成更大的模式也不行"""
"""问题2:报头能否传递更多信息,比如电影大小,名称,评价,简介"""

"""解决方法"""
# 字典作为报头打包(效果好,数字小)
data_dict= {
    'file_name': '野兽先辈摔跤视频.avi',
    'file_size': 1213232433543544667,
    'file_info': '很好康的',
    'file_desc': '经典高清'

}
import json
data_json = json.dumps(data_dict)
print(len(data_json.encode('utf8')))    # 真实字典的长度

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',8083))
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'D:\python9月\代码\day36\03 黏包问题解决方案\有你好看.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'D:\python9月\代码\day36\03 黏包问题解决方案\有你好看.txt', 'rb') as f:
    for i in f:  # 一行行发送 和直接一起发效果一样 因为TCP流式协议的特性
        client.send(i)

# 让其睡眠十秒
import time
time.sleep(10)

UDP协议

"""
1.UDP服务端和客户端'各自玩各自的'
2.UDP不会出现多个消息发送合
"""

# 服务端 import socket server = socket.socket(type=socket.SOCK_DGRAM) # UDP协议 server.bind(('127.0.0.1', 8081)) while True: data, addr = server.recvfrom(1024) print('客户端地址>>>:', addr) print('上述地址发送的消息>>>:', data.decode('utf8')) msg = input('>>>:').strip() server.sendto(msg.encode('utf8'), addr) # 客户端1 import socket client = socket.socket(type=socket.SOCK_DGRAM) server_addr = ('127.0.0.1', 8081) while True: msg = input('>>>:').strip() client.sendto(msg.encode('utf8'), server_addr) data, addr = client.recvfrom(1024) print(data.decode('utf8'), addr) # 客户端2 import socket client = socket.socket(type=socket.SOCK_DGRAM) server_addr = ('127.0.0.1', 8080) while True: msg = input('>>>:').strip() client.sendto(msg.encode('utf8'), server_addr) data, addr = client.recvfrom(1024) print(data.decode('utf8'), addr)

 

标签:socket,黏包,编程,网络,len,server,dict,报头,data
From: https://www.cnblogs.com/juzijunjun/p/16900635.html

相关文章

  • 并发编程
    并发编程粘包现象1.粘包现象产生的本质sock.send(b'jason')data=client.recv(1024)print(data)对方只给了5个字符,但是后面我们返回是要返回1024个粘包现象的产......
  • 网络编程之黏包与struct模块、并发编程
    黏包现象1.服务端连续执行三次recv2.客户端连续执行三次send问题:服务端一次性接收到了客户端三次的消息,这种现象称为'黏包现象'黏包现象产生的原因1.不知道每次的数......
  • 并发编程理论
    操作系统发展史1.穿孔卡片阶段计算机很庞大,使用很麻烦,一次只能给一个人使用,期间很多时候计算机都不工作好处:程序员独占计算机,为所欲为 坏处:计算机利用率太低,浪费......
  • 并发编程理论之多道技术、进程
    并发编程理论之多道技术、进程操作系统的发展穿孔卡片1946年第一台计算机诞生--20世纪50年代中期,还未出现操作系统,计算机工作采用手工操作方式。程序员将对应用程序......
  • day36.黏包现象
    TCP.UDP大致回顾TCP 可靠协议 三次握手建立连接 1.洪水攻击 2.消息反馈 四次挥手断开连接 1.time_wait UDP 不可靠协议"""TCP类似于打电话双方连接......
  • C++PrimerPlus中文第六版第7章编程练习答案
    1、#include<iostream>usingnamespacestd;doublecomputeHarmonicMean(doublex,doubley);intmain(){doublex,y;cout<<"Entertwonumbers(x!=0......
  • 黏包
    黏包现象1.何为黏包现象黏包现象就是客户端连续多次执行send,服务端连续执行相同次数的recv想对应接收客户端发送过来的数据,但是服务端却一次性接受了客户端发来的数......
  • 网络并发3
    今日内容详细粘包现象1.服务端连续执行三次recv2.客户端连续执行三次send问题:服务端一次性接收到了客户端三次的消息该现象称为'粘包现象'粘包现象产生的原因 1.......
  • 黏包 struct模块 进程理论进程的并行与并发
    今日内容黏包现象1.服务端连续三次执行recv2.客户端连续三次执行send问题:服务端一次性接收到了客户端三次消息该现象称为"黏包现象"黏包现象产生的原因 1.不知道......
  • 并发编程
    目录黏包现象struct模块struct.pack()struct.unpack()文件过大无法打包黏包问题解决黏包实战UDP协议(了解)操作系统发展史穿孔卡片阶段联机批处理系统脱机批处理系统并发......