• 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(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()
    以下解决黏包现象,实现大文件的传输,比较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(b'123',('127.0.0.1',8080))
    5 sk.sendto(b'abc',('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_客户端
  • 相关阅读:
    【每日一题】16.Treepath (LCA + DP)
    M-SOLUTIONS Programming Contest 2020 游记 (AB水题,CD模拟,E题DFS)
    关于“Github上传以及Clone时发生的 Failed to connect to github.com port 443: Timed out 问题解法记录
    【离散数学】学习笔记目录
    【每日一题】15.Xorto (前缀和枚举)
    【动态规划】动态规划基础 (OI wiki)
    【每日一题】14.Accumulation Degree(树形DP + 二次扫描)
    AtCoder Beginner Contest 199 游记(AB水题,C字符串操作,D搜索,E状压)
    JXUST_NC
    LinkedList源码阅读笔记
  • 原文地址:https://www.cnblogs.com/ZN-225/p/9146217.html
Copyright © 2020-2023  润新知