网络编程 黏包现象

黏包问题

复制代码
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)
复制代码

 

posted @   橘子菌菌n  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示