zoukankan      html  css  js  c++  java
  • python之路--subprocess,粘包现象与解决办法,缓冲区

    一. subprocess 的简单用法

    import subprocess
    sub_obj = subprocess.Popen(
        'dir',  #系统指令
        shell=True,  #固定方法
        stdout=subprocess.PIPE, #标准输出  PIPE 管道,保存着指令的执行结果
        stderr=subprocess.PIPE  #标准错误输出
    )
    # dir 当前操作系统(Windows)的命令,会执行stdout
    print('正确输出',sub_obj.stdout.read().decode('gbk'))
    # 如果是 'ls' 是Linux里的命令 会执行stderr ,因为系统的编码是gbk
    print('错误输出',sub_obj.stderr.read().decode('gbk'))

    二 .两种粘包现象

      1 连续的小包可能会被优化算法给组合到一起进行发送

    # 客户端
    
    import socket
    BUFSIZE=1024
    ip_port=('127.0.0.1',8080)
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    # res=s.connect_ex(ip_port)
    res=s.connect(ip_port)
    # 这边分两段发送
    s.send('hi'.encode('utf-8'))
    s.send('meinv'.encode('utf-8'))
    
    # 服务端
    
    from socket import *
    ip_port=('127.0.0.1',8080)
    tcp_socket_server=socket(AF_INET,SOCK_STREAM)
    tcp_socket_server.bind(ip_port)
    tcp_socket_server.listen(5)
    conn,addr=tcp_socket_server.accept()
    #服务端连接接收两个信息
    data1 = conn.recv(10)
    data2 = conn.recv(10)
    # 如果网络良好的话 收到的应该是 一条信息himeinv
    print('----->',data1.decode('utf-8'))
    print('----->',data2.decode('utf-8'))
    conn.close()

      2 第一次如果发送的数据大小2000B接收端一次性接受大小为1024,这就导致剩下的内容会被下一次recv接收到,导致结果错乱

    # 客户端
    
    import socket
    
    client = socket.socket()
    client.connect(('127.0.0.1',8001))
    while 1:
        cmd = input('请输入指令:')
    
        client.send(cmd.encode('utf-8'))
        # 这里写1025是因为粘包的原因需要写1025才能正好接收到完整的字
        server_cmd_result = client.recv(1025)
    
        print(server_cmd_result.decode('gbk'))
    
    
    # 服务端
    
    import socket
    import subprocess
    server = socket.socket()
    ip_port = ('127.0.0.1',8001)
    server.bind(ip_port)
    server.listen()
    conn,addr = server.accept()
    while 1:
        from_client_cmd = conn.recv(1024)
        print(from_client_cmd.decode('utf-8'))
        sub_obj = subprocess.Popen(
            from_client_cmd.decode('utf-8'),
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE
        )
        std_msg = sub_obj.stdout.read()
        print('指令的执行结果长度>>>>',len(std_msg))
        conn.send(std_msg)

    三 . 解决粘包现象的方法

      方案一:由于双方不知道对方发送数据的长度,导致接收的时候,可能接收不全,或者多接收另外一次发送的信息内容,所以在发送真实数据之前,

        要先发送数据的长度,接收端根据长度来接收后面的真实数据,但是双方有一个交互确认的过程.

    # 客户端
    
    import socket
    client = socket.socket()
    client.connect(('127.0.0.1',8001))
    while 1:
        cmd = input('请输入指令:')
        client.send(cmd.encode('utf-8'))
        server_res_len = client.recv(1024).decode('utf-8')
        print('来自服务端的消息长度',server_res_len)
        # 告知服务端已经接收到了长度
        client.send(b'ok')
        # 接收所有发过来的所有字节的长度
        server_cmd_result = client.recv(int(server_res_len))
        print(server_cmd_result.decode('gbk'))
    
    # 服务端
    
    import socket
    import subprocess
    server = socket.socket()
    ip_port = ('127.0.0.1',8001)
    server.bind(ip_port)
    server.listen()
    conn,addr = server.accept()
    while 1:
        from_client_cmd = conn.recv(1024)
        print(from_client_cmd.decode('utf-8'))
        #接收到客户端发送来的系统指令,我服务端通过subprocess模块到服务端自己的系统里面执行这条指令
        sub_obj = subprocess.Popen(
            from_client_cmd.decode('utf-8'),
            shell=True,
            stdout=subprocess.PIPE,  #正确结果的存放位置
            stderr=subprocess.PIPE   #错误结果的存放位置
        )
        #从管道里面拿出结果,通过subprocess.Popen的实例化对象.stdout.read()方法来获取管道中的结果
        std_msg = sub_obj.stdout.read()
        #为了解决黏包现象,我们统计了一下消息的长度,先将消息的长度发送给客户端,客户端通过这个长度来接收后面我们要发送的真实数据
        std_msg_len = len(std_msg)
        #首先将数据长度的数据类型转换为bytes类型
        std_bytes_len = str(len(std_msg)).encode('utf-8')
        print('指令的执行结果长度>>>>',len(std_msg))
        conn.send(std_bytes_len)
        # 确认一下客户端是否收到
        status = conn.recv(1024)
        if status.decode('utf-8') == 'ok':
            conn.send(std_msg)
        else:
            pass

      方案二:

        struct模块,

        打包:struct.pack(‘i’,长度)

        解包:struct.unpack(‘i’,字节)

    # struct 的简单用法
    import struct
    num = 100
    #打包,将int类型的数据打包成4个长度的bytes类型的数据
    byt = struct.pack('i',num)
    print(byt)  # b'dx00x00x00'
    #解包,将bytes类型的数据,转换为对应的那个int类型的数据
    # int_num = struct.unpack('i',byt)
    # print(int_num)  # (100,)
    int_num = struct.unpack('i',byt)[0]
    print(int_num) # 100

     

    # 客户端
    
    import socket
    import struct
    client = socket.socket()
    client.connect(('127.0.0.1',8001))
    
    while 1:
        cmd = input('请输入指令:')
        #发送指令
        client.send(cmd.encode('utf-8'))
        #接收数据长度,首先接收4个字节长度的数据,因为这个4个字节是长度
        server_res_len = client.recv(4)
        msg_len = struct.unpack('i',server_res_len)[0]
        print('来自服务端的消息长度',msg_len)
        #通过解包出来的长度,来接收后面的真实数据
        server_cmd_result = client.recv(msg_len)
        print(server_cmd_result.decode('gbk'))
    
    
    # 服务端
    
    import socket
    import subprocess
    import struct
    server = socket.socket()
    ip_port = ('127.0.0.1',8001)
    server.bind(ip_port)
    server.listen()
    conn,addr = server.accept()
    while 1:
        from_client_cmd = conn.recv(1024)
        print(from_client_cmd.decode('utf-8'))
        #接收到客户端发送来的系统指令,我服务端通过subprocess模块到服务端自己的系统里面执行这条指令
        sub_obj = subprocess.Popen(
            from_client_cmd.decode('utf-8'),
            shell=True,
            stdout=subprocess.PIPE,  #正确结果的存放位置
            stderr=subprocess.PIPE   #错误结果的存放位置
        )
        #从管道里面拿出结果,通过subprocess.Popen的实例化对象.stdout.read()方法来获取管道中的结果
        std_msg = sub_obj.stdout.read()
        #为了解决黏包现象,我们统计了一下消息的长度,先将消息的长度发送给客户端,客户端通过这个长度来接收后面我们要发送的真实数据
        std_msg_len = len(std_msg)
        print('指令的执行结果长度>>>>',len(std_msg))
        msg_lenint_struct = struct.pack('i',std_msg_len)
        conn.send(msg_lenint_struct+std_msg)

    四 . 缓冲区

      

  • 相关阅读:
    SQL Server 实现Split函数
    15.java设计模式之访问者模式
    14.java设计模式之命令模式
    13.java设计模式之模板模式
    12.java设计模式之代理模式
    11.java设计模式之享元模式
    10.java设计模式之外观模式
    9.java设计模式之组合模式
    8.java设计模式之装饰者模式
    7.java设计模式之桥接模式
  • 原文地址:https://www.cnblogs.com/attila/p/10221261.html
Copyright © 2011-2022 走看看