zoukankan      html  css  js  c++  java
  • 1018 笔记

    subprocess

    1.定义

    1.可以帮你通过代码执行操作系统的终端命令
    2.并返回终端执行命令后的结果
    

    2.方法

    subprocess 模块来运行系统命令.subprocess模块允许我们创建子进程,连接他们的输入/输出/错误管道,还有获得返回值。

    1.subprocess模块中只定义了一个类: Popen。可以使用Popen来创建进程,并与进程进行复杂的交互。
    
    2.如果参数shell设为true,程序将通过shell来执行。
    
    3.subprocess.PIPE
      在创建Popen对象时,subprocess.PIPE可以初始化stdin, stdout或stderr参数。表示与子进程通信的标准流。
    

    3.代码

    初始版

    import subprocess
    
    # 执行系统dir命令,把执行的正确结果放到管道中
    obj = subprocess.Popen(
        'tasklist',  # cmd 命令  /dir/tasklist
        shell= True,    #  Shell=True
        stderr=subprocess.PIPE, #  返回错误结果参数 error
        stdout=subprocess.PIPE  #  返回正确结果参数
    )
    # 拿到正确结果的管道,读出里面的内容
    data = obj.stdout.read() + obj.stderr.read()
    # cmd中默认为gkb,解码需要gbk
    print(data.decode('gbk'))
    
    

    客户端与服务端交互,cmd命令在客户端打印

    '''服务端'''
    import socket
    import subprocess
    '''客户端输入cmd命令,服务端接受命令并传给cmd,得到正确的数据,利用subprocess返回数据'''
    
    s = socket.socket()
    
    s.bind(
        ('127.0.0.1',8848)
    )
    s.listen(5)
    print('等待客户端连接')
    
    while True:
        conn,addr = s.accept()
        print(f'有客户端{addr}成功连接')
        while True:
            try:
                # 1.接受用户输入的cmd命令
                cmd = conn.recv(1024).decode('utf-8')
                if cmd == 'q':
                    break
                # 2.将用户输入命令利用subprocess得到正确返回
                obj = subprocess.Popen(
                    cmd,
                    shell=True,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE
                )
                # 3.取出正确和错误结果
                data = obj.stdout.read() + obj.stderr.read()
                # 将结果发送给客户端
                conn.send(data)
            except Exception:
                break
        conn.close()
    ==========================================================
    '''客户端'''
    
    import socket
    import subprocess
    
    c = socket.socket()
    c.connect(
        ('127.0.0.1',8848)
    )
    while True:
        data = input('请输入CMD命令:')
        # 发送
        c.send(data.encode('utf-8'))
        if  data == 'q':
            break
        # 接收 进行解码,cmd默认gbk形
        msg = c.recv(1024).decode('gbk')
        print(msg)
    

    粘包问题

    服务端第一次发送的数据,客户端无法精确一次性接受完毕,下一次发送的数据与上一次数据粘在一起了.

    1. 无法预测对方需要接受的数据大小长度
    2. 多次连续发送数据量小,并且时间间隔短的数据一次性打包发送
    在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的。因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小、数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。
    
    对于UDP,不会使用块的合并优化算法,这样,实际上目前认为,是由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。所以UDP不会出现粘包问题。
    

    TCP协议特性

    TCP是一个流式协议,会将多次连续发送数据量小,并时间间隔短的数据一次性打包发送.

    解决粘包问题

    为了避免粘包现象,可采取以下几种措施:

    (1)对于发送方引起的粘包现象,用户可通过编程设置来避免,TCP提供了强制数据立即传送的操作指令push,TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满;
    
    (2)对于接收方引起的粘包,则可通过优化程序设计、精简接收进程工作量、提高接收进程优先级等措施,使其及时接收数据,从而尽量避免出现粘包现象;
    
    (3)由接收方控制,将一包数据按结构字段,人为控制分多次接收,然后合并,通过这种手段来避免粘包。
    

    struct模块

    可以将发送的数据长度提前发送至服务端,服务端接受到数据长度,自定义接受.

    必须先定义报头,发送报头,再发送真实数据.
    
    是一个可以将很长的数据的长度,压缩成固定的长度的一个标记(数据报头)
    
    `i:模式`,会将数据长度压缩成4个bytes
    

    代码

    '''服务端'''
    import socket
    import struct
    import subprocess
    
    s= socket.socket()
    s.bind(
        ('127.0.0.1',8848)
    )
    
    s.listen(5)
    print('等待客户端连接')
    while True :
        conn,addr= s.accept()
        print(f'客户端{addr}已连接')
        while True:
            try:
                # 1.接受用户输入的cmd命令
                cmd = conn.recv(1024).decode('utf-8')
                if cmd == 'q':
                    break
                # 2.将用户输入命令利用subprocess得到正确返回
                obj = subprocess.Popen(
                    cmd,
                    shell=True,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE
                )
                # 3.取出正确和错误结果
                data = obj.stdout.read() + obj.stderr.read()
                # 4.打包压缩,成只有4位的报头信息,i 模式
                headers = struct.pack('i',len(data))
                # 5.将报头先行发送,让客户端准备接受的大小
                conn.send(headers)
                # 6.再发送真实数据
                conn.send(data)
            except Exception as e:
                print(e)
                break
        conn.close()
    =========================================================
    '''客户端'''
    import socket
    import subprocess
    import struct
    
    c = socket.socket()
    c.connect(
        ('127.0.0.1',8848)
    )
    while True:
        msg = input('请输入cmd命令')
        if msg == 'q':
            break
        # 1.将cmd 命令传至服务端处理
        c.send(msg.encode('utf-8'))
        # 2.接受服务端发送的4位报头
        headers = c.recv(4)
        # 3.将报头解包(unpack),获得元组,索引取数据长度
        data_len = struct.unpack('i',headers)[0]
        # 4.接受真实的数据信息
        data = c.recv(data_len)
        print(data.decode('gbk'))
    c.close()
    
    

    代码2

    即想发送文件,又想发送文件的描述信息

    '''客户端发送字典给服务端
    send_dic:{
    	file_name : 文件名
    	file_size : 文件的真实长度}
    服务端接受到字典,并接受文件的真实数据'''
    
    服务端
    
    import socket
    import struct
    import json
    
    s = socket.socket()
    s.bind(
        ('127.0.0.1',8848)
    )
    s.listen(5)
    print('等待客户端连接')
    
    while True:
        conn,addr = s.accept()
        print(f'用户{addr}已连接')
        while True:
            try:
                # 1.接受到客户端发送的报头
                beaders = conn.recv(4)
                # print(beaders)  #  b'Mx00x00x00'
    
                # 2.报头解压缩,索引获得数据长度
                data__len = struct.unpack('i',beaders)[0]
                # print(data__len)    # 77
    
                # 3.接受真实数据(序列化的数据)
                bytes_data = conn.recv(data__len)
                # print(bytes_data)   # b'{"file_name": "abc\u7684\u9017\u6bd4\u4eba\u751f.txt", "file_size": 12345678}'
    
                # 4.反序列化获得数据
                dic = json.loads(bytes_data.decode('utf-8'))
                # print(dic)  #  {'file_name': 'abc的逗比人生.txt', 'file_size': 12345678}
    
            except Exception as e :
                print(e)
                break
        conn.close()
    =========================================================
    '''客户端'''
    import socket
    import struct
    import json
    import time
    
    c = socket.socket()
    c.connect(
        ('127.0.0.1',8848)
    )
    while True:
        # 1.用户文件的字典
        send_dic = {
            'file_name':'abc的逗比人生.txt',
            'file_size':12345678
        }
        # 2.json序列化,并转码成bytes类型数据(为的是struct的len长度)
        json_data = json.dumps(send_dic)
        bytes_data = json_data.encode('utf-8')
        # 3.压缩数据做成报头,发送至服务端
        headers = struct.pack('i',len(bytes_data))
        # print(bytes_data)    # b'{"file_name": "abc\u7684\u9017\u6bd4\u4eba\u751f.txt", "file_size": 12345678}'
        # print(len(bytes_data))    # 77
        # print(headers)    # b'Mx00x00x00'
    
        c.send(headers)
        # 4.发送真实数据
        c.send(bytes_data)
    
        time.sleep(10)
        # {'file_name': 'abc的逗比人生.txt', 'file_size': 12345678}
        # 会一直打印,所以手动停止5秒
    

    上传大文件

    利用while循环进行一段一段的上传防止粘包.

    服务端

    import socket
    import struct
    import json
    
    s = socket.socket()
    s.bind(
        ('127.0.0.1',8848)
    )
    s.listen(5)
    print('等待客户端连接')
    
    
    while True:
        conn, addr = s.accept()
        print(f'客户端{addr}已连接')
        try:
            # 1.接受客户端传来的字典报头
            headers = conn.recv(4)
            # 2.解压索引获得字典的长度
            data_len = struct.unpack('i',headers)[0]
            # 3.接受文件字典的bytes信息
            bytes_data = conn.recv(data_len)
            # 4.反序列化得到字典数据
            data_dic = json.loads(bytes_data.decode('utf-8'))
            print(data_dic)
            # 5.获得文件字典的名字与大小
            file_name = data_dic.get('file_name')
            file_size = data_dic.get('file_size')
    
            # 6.以文件名打开文件(循环控制打开资源占用)
            size = 0
            with open(file_name,'wb') as f:
                while size < file_size:
                    # 每次接受1024大小
                    data = conn.recv(1024)
                    # 每次写入data大小
                    f.write(data)
                    # 写完进行追加
                    size += len(data)
                print(f'{file_name}接受完毕')
        except Exception as e:
            print(e)
            break
    conn.close()
    

    客户端

    import socket
    import struct
    import json
    
    c= socket.socket()
    c.connect(
        ('127.0.0.1',8848)
    )
    # 1.打开一个视频文件,获取数据大小
    with open(r'F:老男孩12期开课视频day 275 上传大文件.mp4','rb') as f :
        movie_bytes = f.read()   #获得的是二进制流
        # 文件自动关闭
    # 2.为视频文件组织一个信息字典,字典有名称大小
    movie_info = {
        'file_name':'大视频.mp4',
        'file_size':len(movie_bytes)
    }
    # 3.打包字典,发送文件字典的报头(客户端获得文件的名字与大小)
    json_data = json.dumps(movie_info)
    bytes_data = json_data.encode('utf-8')
        # 获得字典的报头
    headers = struct.pack('i',len(bytes_data))
        # 发送报头
    c.send(headers)
        # 发送真实文件的字典
    c.send(bytes_data)
    # 4.发送真实的文件数据(大文件循环发送减少占用)
    size = 0
    num = 1
    with open(r'F:老男孩12期开课视频day 275 上传大文件.mp4','rb') as f :
        while size < len(movie_bytes):
            # 打开时每次读取1024大小数据
            send_data = f.read(1024)  # 获得的是二进制流
            print(send_data,num)
            num += 1
            # 每次发送都是1024大小数据
            c.send(send_data)
            # 为初始数据增加发送的大小,控制循环
            size += len(send_data)
    

    UDP

    UDP是一种传输协议,

    1.不需要建立双向通道
    2.不会粘包
    3.客户端给服务端发送数据,不需要等待服务端返回接受成功
    4.数据容易丢失,
    
    - udp就好比是在发短信
    - tcp协议好比是在打电话
    
    
    • UDP是无链接的,先启动哪一端都不会报错
    • UDP协议是数据报协议,发空的时候也会自带报头,因此客户端输入空,服务端也能收到
    '''服务端'''
    import socket
    
    # 1.SOCK_DGRAM:代表UDP
    server = socket.socket(type=socket.SOCK_DGRAM)
    
    # 2.服务端要绑定 ip + port
    server.bind(
        ('127.0.0.1',8848)
    )
    # server.listen(5)  UDB不需要建立连接
    # conn ,addr= server.accept() 也不需要
    # 3.接收服务端发送的消息,及地址
    msg ,addr = server.recvfrom(1024)
    
    print(msg)
    '''只管发送,不会管有没有接收到,不可靠传输'''
    
    =========================================================
    
    '''客户端'''
    import socket
    # 1.获取UDB对象
    client = socket.socket(type=socket.SOCK_DGRAM)
    # 2.获取ip端口地址
    IP_port = ('127.0.0.1',8848)
    # 3.发送数据至服务端地址
    client.sendto(b'hello',IP_port)
    
    
    

    QQ聊天室

    • UPD协议一般不用于传输大数据。
    • UDP套接字虽然没有粘包问题,但是不能替代TCP套接字,因为UPD协议有一个缺陷:如果数据发送的途中,数据丢失,则数据就丢失了,而TCP协议则不会有这种缺陷,因此一般UPD套接字用户无关紧要的数据发送,例如qq聊天。
    			'''服务端'''
    import socket
    server = socket.socket(type=socket.SOCK_DGRAM)
    
    server.bind(
        ('127.0.0.1',8848)
    )
    print('等待用户连接...')
    while True:
        # 接受到客户端发送的消息(元组形式)
        msg ,addr = server.recvfrom(1024)
    
        print(f'来自用户{addr}的消息:')
        print(msg.decode('utf-8'))      # 打印客户端发送的消息
    
        send_msg = input('服务端发送的消息:').encode('utf-8')  # 服务端发送消息至客户端
        # 服务端向客户端addr发送
        server.sendto(send_msg,addr)
        
        ====================================================
      			'''客户端'''
    import socket
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    ip_port = ('127.0.0.1',8848)
    
    while True:
        send_msg = input('客户端1:').encode('utf-8')
    
        #  客户端发送至服务端的消息
        client.sendto(send_msg,ip_port)
    
    
        # 接受到服务端的消息
        msg ,addr= client.recvfrom(1024)
        print(f'来自用户{addr}的消息')
        print(msg.decode('utf-8'))
    
    

    SocketServer

    python内置模块,可以简化socket套接字服务端的代码。

    • 简化TCP与UDP服务端代码
    • 必须要创建一个类

    基于tcp的套接字,关键就是两个循环,一个链接循环,一个通信循环

    socketserver模块中分两大类:server类(解决链接问题)和request类(解决通信问题)

    '''服务端'''
    import socketserver
    # 1.定义类,必须继承BaseRequestHandler类
    class Mytcp(socketserver.BaseRequestHandler):
        # 2.重写父类handle
        def handle(self):
            print(self.client_address)
            while True:
                try:
                    # 1.服务端接受消息
                    data = self.request.recv(1024).decode('utf-8')  # 等同于conn.recv(1024)
                    send_msg = data.upper()
                    # 2.给客户端发送消息
                    self.request.send(send_msg.encode('utf-8'))
                except Exception as e:
                    print(e)
                    break
    
    if __name__ == '__main__':
        # 定义类的传参得到实例化对象
        server = socketserver.ThreadingTCPServer(
            ('127.0.0.1',8848),Mytcp
        )
        server.serve_forever()
        
    ==========================================================
    '''客户端'''
    import socket
    
    client = socket.socket()
    
    client.connect(
        ('127.0.0.1',8848)
    )
    while True:
        msg = input('客户端向服务端发送:')
        client.send(msg.encode('utf-8'))
        if msg == 'q':
            break
    
        data = client.recv(1024)
        print(data.decode('utf-8'))
    
    
  • 相关阅读:
    SGU 127. Telephone directory --- 模拟
    SGU 126. Boxes --- 模拟
    SGU 128. Snake --- 暴力枚举+并查集+贪心+计算几何
    Codeforces Round #246 (Div. 2) B. Football Kit
    Codeforces Round #252 (Div. 2) C. Valera and Tubes
    Codeforces Round #251 (Div. 2) C. Devu and Partitioning of the Array
    codeforces --- Round #250 (Div. 2) B. The Child and Set
    codeforces --- Round #250 (Div. 2) A. The Child and Homework
    AFNetworking 动态修改acceptableContentTypes 设置ContentType
    swift pod第三方OC库使用use_frameworks!导致#import提示无法找到头文件
  • 原文地址:https://www.cnblogs.com/fwzzz/p/11701180.html
Copyright © 2011-2022 走看看