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()
View Code

    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()
View Code

 

解决黏包问题

  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()
View Code

    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()
View Code

  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()
View Code

    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()
View Code
posted @ 2019-12-19 22:16  四方游览  阅读(179)  评论(0)    收藏  举报