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()
UDP通信客户端(调用自己的解码编码类)

 

  (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()
UDP通信服务器端(调用自己的解码编码类)

 

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(bsuccess)# 给客户端回复一个成功,代表字典接收成功了,也是把字典内容和接下来要接收的文件内容分割开
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()
以下解决黏包现象,实现大文件的传输,比较low比的方法

 

  (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()
比较高端的方法,使用struct

 

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()
UDP_黏包_客户端

 

  (2) UDP_黏包_客户端

技术分享图片
1 import socket
2 sk = socket.socket(type=socket.SOCK_DGRAM)
3 
4 sk.sendto(b123,(127.0.0.1,8080))
5 sk.sendto(babc,(127.0.0.1,8080))
6 sk.close()
UDP_黏包_客户端

 

  (3) TCP_nagle_服务器端

技术分享图片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))
TCP_nagle_客户端

 

以上是关于Python全栈__编写UDP通信编解码类文件的上传远程执行命令黏包的主要内容,如果未能解决你的问题,请参考以下文章

Python cx_Freeze __init__“没有名为编解码器的模块”

9python全栈之路-模块与包

Python全栈-day14-模块和包

python 2.x编解码

Python学习笔记__16.3章 UDP编程

Python全栈开发记录_第九篇(类的基础_封装_继承_多态)