Python全栈__编写UDP通信编解码类文件的上传远程执行命令黏包
Posted 芒果不盲
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python全栈__编写UDP通信编解码类文件的上传远程执行命令黏包相关的知识,希望对你有一定的参考价值。
1、UDP通信编解码类
(1) 类
1 # ------------------UDP通信解码编码类------------------------ 2 3 from socket import * 4 5 class My_Socket(socket): 6 def __init__(self,coding=‘utf-8‘): 7 self.coding = coding 8 super(My_Socket, self).__init__(type=SOCK_DGRAM) 9 10 def my_recv(self,num): 11 msg,addr = self.recvfrom(num) 12 return msg.decode(self.coding),addr 13 14 def my_send(self,msg,addr): 15 return self.sendto(msg.encode(self.coding),addr)
(2) UDP通信客户端(调用自己的解码编码类)
# --------------UDP通信客户端(调用自己的解码编码类)----------------- from MySocket import My_Socket sk = My_Socket() msg_s = input(‘>>>‘) sk.my_send(msg_s,(‘127.0.0.1‘,8080)) sk.close()
(3) UDP通信服务器端(调用自己的解码编码类)
1 # -----------UDP通信服务器端(调用自己的解码编码类)--------------- 2 3 from MySocket import My_Socket 4 5 sk = My_Socket() 6 7 sk.bind((‘127.0.0.1‘,8080)) 8 9 msg_r,addr = sk.my_recv(1024) 10 11 print(msg_r) 12 13 sk.close()
2、文件的上传(服务器端)
(1)只能上传小文件。
1 import socket 2 import json 3 sk = socket.socket() 4 5 sk.bind((‘127.0.0.1‘,8080)) 6 7 sk.listen(5) 8 9 conn,addr = sk.accept() 10 11 # 上传下载的逻辑 12 str_dic = conn.recv(1024).decode(‘utf-8‘) 13 dic = json.loads(str_dic) 14 # dic = {‘opt‘:ls.get(num),‘filename‘:filename,‘content‘:content} 15 if dic[‘opt‘] == ‘upload‘: 16 filename = ‘new_‘+dic[‘filename‘] 17 with open(filename,‘w‘,encoding=‘utf-8‘) as f: 18 f.write(dic[‘content‘]) 19 20 conn.close() 21 sk.close()
(2)通过方法:先获取文件大小,然后把文件大小加入到字典中,把字典发过去接收端就可以知道文件大小了,就可以根据文件大小进行接收大文件了。
问题:可能黏包的问题,就是send了字典之后,就send了文件内容。
解决的方法:在两个send之间加了一个recv,接收一个success 。
1 import socket 2 import json 3 sk = socket.socket() 4 5 sk.bind((‘127.0.0.1‘,8080)) 6 7 sk.listen(5) 8 9 conn,addr = sk.accept() 10 11 # 上传下载的逻辑 12 str_dic = conn.recv(1024).decode(‘utf-8‘)# 先接受到字符串形式的字典 13 conn.send(b‘success‘)# 给客户端回复一个成功,代表字典接收成功了,也是把字典内容和接下来要接收的文件内容分割开 14 dic = json.loads(str_dic)# 反序列化,将字典还原出来 15 # dic = {‘opt‘:ls.get(num),‘filename‘:filename,‘content‘:content} 16 if dic[‘opt‘] == ‘upload‘: 17 filename = ‘new_‘+dic[‘filename‘]# 产生一个新的文件名 18 filesize = dic[‘filesize‘]# 获取文件的大小 19 with open(filename,‘wb‘) as f: 20 while filesize: 21 content = conn.recv(1024) 22 filesize -= len(content)# 减去接收到的长度,而不能直接减去1024 23 f.write(content) 24 25 conn.close() 26 sk.close()
(3)先把字典的大小获取,然后server先接收字典的大小,然后根据字典大小去接收字典,这样就直接解决了2中的问题。
1 import socket 2 import json 3 sk = socket.socket() 4 5 sk.bind((‘127.0.0.1‘,8080)) 6 7 sk.listen(5) 8 9 conn,addr = sk.accept() 10 11 12 # 我算出来了字典长度为70(属于作弊),这个‘70’是两个字节的长度,所以接收两个字节长度,把字典大小接收到 13 len_dic = int(conn.recv(2).decode(‘utf-8‘))#在这里转成int以便下一行代码中当做recv的参数 14 # 这个len_dic = 70,转码后是字符串 15 16 str_dic = conn.recv(len_dic).decode(‘utf-8‘)# 根据字典长度去接收字典,防止接收到文件的内容 17 18 # 在这里,上边代码我明确了字典大小,就可以按照字典大小接收固定长度,这样之后,就可以防止上边接收到 下边应该接收的文件内容 19 # 此时我就可以省略一个 sk.send(b‘success‘)这句话,就可以少一个网络传输数据时的延时。 20 dic = json.loads(str_dic) 21 # dic = {‘opt‘:ls.get(num),‘filename‘:filename,‘filesize‘:filesize} 22 if dic[‘opt‘] == ‘upload‘: 23 filename = ‘new_‘+dic[‘filename‘] 24 filesize = dic[‘filesize‘] 25 with open(filename,‘wb‘) as f: 26 while filesize: 27 content = conn.recv(1024) 28 filesize -= len(content) 29 f.write(content) 30 31 conn.close() 32 sk.close()
(4)字典的大小不固定,所以我使用了struct模块中的pack方法,解决这个事情,使字典的大小这个数字无论多少位,我都固定生成一个4位的bytes,这样server端就可以直接先接收4个字节了,通过unpack获取字典大小。
1 import socket 2 import json 3 import struct 4 sk = socket.socket() 5 6 sk.bind((‘127.0.0.1‘,8080)) 7 8 sk.listen(5) 9 10 conn,addr = sk.accept() 11 12 13 # 上边说了,如果我在这里明确接收2个字节就可以接收到字典的大小,这是作弊 14 # 因为字典大小如果是 100~999 ,我这里就要接收3个字节 15 # 如果字典大小是 100000 ~ 999900 ,我这里就要接收6个字节 16 # 所以为了公正,我使用了struct,在客户端先把‘字典大小’的长度固定为4个字节 17 len_dic = conn.recv(4)# 此时接收到了 字典的长度,不过是经过struct.pack处理后的 18 len_str_dic = struct.unpack(‘i‘,len_dic)[0] # 在这里,得到int型的字典长度,注意:unpack方法返回一个元组。 19 20 str_dic = conn.recv(len_str_dic).decode(‘utf-8‘)# 根据字典长度去接收字典,防止接收到文件的内容 21 22 23 dic = json.loads(str_dic) 24 # dic = {‘opt‘:ls.get(num),‘filename‘:filename,‘filesize‘:filesize} 25 if dic[‘opt‘] == ‘upload‘: 26 filename = ‘new_‘+dic[‘filename‘] 27 filesize = dic[‘filesize‘] 28 with open(filename,‘wb‘) as f: 29 while filesize: 30 content = conn.recv(1024) 31 filesize -= len(content) 32 f.write(content) 33 34 conn.close() 35 sk.close()
3、远程执行命令
(1) 远程执行命令(服务器端)
1 # ------------------远程执行命令(服务器端)------------------------ 2 3 import socket 4 import subprocess 5 6 sk = socket.socket() 7 sk.bind(("127.0.0.1", 8080)) 8 sk.listen(5) 9 10 conn, addr = sk.accept() 11 12 while 1: 13 cmd = conn.recv(1024).decode(‘utf-8‘) 14 r = subprocess.Popen(cmd, shell=True, 15 stdout=subprocess.PIPE, 16 stderr=subprocess.PIPE) 17 stdout = r.stdout.read() 18 stderr = r.stderr.read() 19 if stdout: 20 conn.send(stdout) 21 elif stderr: 22 conn.send(stderr) 23 24 conn.close() 25 sk.close()
(2) 远程执行命令(客户端)
1 # ---------------远程执行命令(客户端)-------------------- 2 3 import socket 4 5 sk = socket.socket() 6 7 sk.connect((‘127.0.0.1‘, 8080)) 8 9 while 1: 10 cmd = input(‘>>>‘) 11 sk.send(cmd.encode(‘utf-8‘)) 12 ret_1 = sk.recv(1024).decode(‘gbk‘) 13 ret_2 = sk.recv(1024).decode(‘gbk‘) 14 print(ret_1) 15 16 sk.close()
4、黏包
黏包:是指可能是:接收端和发送端两端在进行收发时,数据大小不统一造成的.
也可能是:合包机制和nagle算法的实现,而没有提供科学的拆包机制,造成的.
一种是因为:client连续发送少量数据,时间间隔短,所以nagle算法帮你合包了,但是此时server不知道要接收多少,所以造成数据混乱
一种是因为:client直接发送了大量数据,但是server端接收的长度不够,所以造成数据混乱
(1) UDP_黏包_服务器
1 import socket 2 3 sk = socket.socket(type=socket.SOCK_DGRAM) 4 5 sk.bind((‘127.0.0.1‘, 8080)) 6 7 msg, addr = sk.recvfrom(1024) 8 9 print(msg) 10 11 sk.close()
(2) UDP_黏包_客户端
1 import socket 2 sk = socket.socket(type=socket.SOCK_DGRAM) 3 4 sk.sendto(b‘123‘,(‘127.0.0.1‘,8080)) 5 sk.sendto(b‘abc‘,(‘127.0.0.1‘,8080)) 6 sk.close()
(3) TCP_nagle_服务器端
(4) TCP_nagle_客户端
1 import socket 2 3 BUFSIZE = 1024 4 ip_port = (‘127.0.0.1‘, 8080) 5 6 s = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) 7 res = s.connect_ex(ip_port) 8 9 s.send(‘hello egg‘.encode(‘utf-8‘))
以上是关于Python全栈__编写UDP通信编解码类文件的上传远程执行命令黏包的主要内容,如果未能解决你的问题,请参考以下文章