Python网络编程基础 ❸ struct模块 解决黏包问题
struct模块
1、demo代码
1 import struct 2 3 # 将一个数字打包,转为bytes 4 len = struct.pack('i', 123434) # 第一参数i,表示整数,即第二个传入的数据类型。i最常用 5 print(len) 6 7 # 解开包,是一个两位的元组 8 re = struct.unpack('i', len) 9 print(re[0])
2、上传和下载文件演示
1)服务端
1 import socket,json,struct 2 3 sk = socket.socket() 4 sk.bind(('127.0.0.1', 8888)) 5 sk.listen(5) 6 7 request = sk.accept()[0] 8 9 # 接收数据长度 10 msg_len = request.recv(4) 11 data_len = struct.unpack('i', msg_len) 12 13 # 接收数据 14 data = request.recv(int(data_len[0])).decode('utf-8') # 15 print(data) 16 dic = json.loads(data) 17 18 if dic['operate'] == 'upload': 19 with open(dic['filename'], 'wb') as f: 20 f.write(dic['content'].encode('utf-8')) 21 elif dic['operate'] == 'download': 22 pass 23 24 request.close() 25 sk.close()
2)客户端(上传)
1 import socket,json,struct 2 from os import path 3 4 # 客户端进行上传和下载 5 6 sk = socket.socket() 7 sk.connect(('127.0.0.1', 8888)) 8 9 10 def get_filename(file_path): 11 ''' 12 获取路径中的文件名 13 :param file_path: 14 :return: 15 ''' 16 return path.basename(file_path) 17 18 operate = ['upload', 'download'] 19 for i, oper in enumerate(operate, 1): 20 print('%d %s'%(i, oper)) 21 num = input('请输入操作对应的序号:>>>').strip() 22 23 if num == '1': 24 '''上传操作''' 25 file_path = r'C:\Users\台风\Documents\bar.job' # 文件名不能是中文! 26 # 告诉对方要传数据的大小 27 file_size = struct.pack('i', 4096) 28 sk.send(file_size) 29 # 告诉对方要上传的文件的名字 30 filename = get_filename(file_path) 31 # 准备数据 32 with open(file_path, encoding='utf-8') as f: 33 data = f.read() 34 # 用字典将所有报头和数据封装在一起 35 dic = {'operate': 'upload', 'filename': filename, 'content': data} 36 # 将字典使用json序列化,用于传输 37 dic_json = json.dumps(dic) 38 print(dic_json) 39 sk.send(dic_json.encode('utf-8')) 40 elif num == '2': 41 '''下载操作''' 42 pass 43 44 sk.close()
解决黏包问题
1、初步解决,发送数据前先将其大小发过去
1)server端
1 import socket 2 3 sk = socket.socket() 4 sk.bind(('127.0.0.1', 8888)) 5 sk.listen() 6 7 conn, addr = sk.accept() 8 msg = conn.recv(1024).decode('utf-8') # 先接收数据长度数据 9 10 data = conn.recv(int(msg)) # 根据长度设定缓存大小 11 print(data.decode('utf-8')) 12 13 data2 = conn.recv(int(msg)) 14 print(data2.decode('utf-8')) 15 16 conn.close() 17 sk.close()
2)客户端
1 import socket, time 2 # 黏包的原因是没有给定要传信息的长度,如果事先知道就不会出现了 3 # 所以在每次传递前,发送长度消息 4 sk = socket.socket() 5 ip_port = ('127.0.0.1', 8888) 6 sk.connect(ip_port) 7 8 sk.send(b'4') 9 time.sleep(0.00001) # 发送数据长度,与后面的发送数据之间,要隔开一段时间,否者会跟后面的数据黏包; 10 # 但后面的数据就不会了,如果发送的数据大于4,会丢弃! 11 12 sk.send(b'hero') 13 sk.send(b'goodbye') 14 15 sk.close()
2、使用struct解决远程执行dos命令时的黏包问题
1)服务器端
1 import socket,struct 2 3 sk = socket.socket() 4 sk.bind(('127.0.0.1', 8080)) 5 sk.listen() 6 7 conn, addr = sk.accept() 8 9 while 1: 10 ipt = input('>>>').encode('gbk') 11 conn.send(ipt) 12 msg = conn.recv(4) 13 data_length = struct.unpack('i', msg)[0] 14 data = conn.recv(data_length).decode('gbk') 15 print(data) 16 17 conn.close() 18 19 sk.close()
2)客户端
1 import socket, subprocess, struct 2 3 sk = socket.socket() 4 sk.connect(('127.0.0.1', 8080)) 5 6 while 1: 7 msg = sk.recv(1024).decode('utf-8') 8 print(msg) 9 sp = subprocess.Popen(msg, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 10 # 得到要发送数据的长度 11 std_err = sp.stderr.read() 12 std_out = sp.stdout.read() 13 data_length = len(std_err+std_out) 14 15 # 使用struct 16 length = struct.pack('i', data_length) 17 # 发送数据长度 18 sk.send(length) 19 20 sk.send(std_err) 21 sk.send(std_out) 22 23 sk.close()

浙公网安备 33010602011771号