zoukankan      html  css  js  c++  java
  • python粘包分析与解决

    TCP与UDP协议

    1. TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的
    2. UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的
    3. tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头,实验略

    粘包现象

    socket收发消息的原理

    应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。

    而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。怎样定义消息呢?

    可以认为对方一次性write/send的数据为一个消息,需要明白的是当对方send一条信息的时候,无论底层怎样分段分片,TCP协议层会把构成整条消息的数据段排序完成后才呈现在内核缓冲区。

    #1:不管是recv还是send都不是直接接收对方的数据,而是操作自己的操作系统内存--->不是一个send对应一个recv
    #2:recv:
            wait data 耗时非常长
            copy data
    
        send:
            copy data

    例如基于tcp的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看了,根本不知道该文件的字节流从何处开始,在何处结束

    所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

    只有TCP有粘包现象,UDP永远不会粘包

    粘包不一定会发生:

    如果发生了:1.可能是在客户端已经粘了

          2.客户端没有粘,可能是在服务端粘了

     客户端粘包:

    发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据量很小,TCP优化算法会当做一个包发出去,产生粘包)

    client端:
    import socket
    import time
    
    client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
    client.connect(('127.0.0.1',9904))
    
    
    client.send('hello'.encode('utf-8'))
    client.send('world'.encode('utf-8'))
    
    server端:
    import socket
    import time
    
    server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    server.bind(('127.0.0.1',9904)) #0-65535:0-1024给操作系统使用
    server.listen(5)
    
    
    conn,   addr=server.accept()
    print('connect by ',addr)
    res1 = conn.recv(100)
    print('第一次',res1)
    res2=conn.recv(10)
    print('第二次', res2)
    

     服务端输出结果

    connect by  ('127.0.0.1', 9787)
    第一次 b'helloworld'
    第二次 b''
    

     

    服务端粘包

    接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包) 

    server端:
    import socket
    import time
    server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    server.bind(('127.0.0.1',9904)) #0-65535:0-1024给操作系统使用
    server.listen(5)
    
    
    conn,   addr=server.accept()
    print('connect by ',addr)
    res1 = conn.recv(2)#第一没有接收完整
    print('第一次',res1)
    time.sleep(6)
    res2=conn.recv(10)# 第二次会接收旧数据,再收取新的
    print('第二次', res2)
    
    client端
    import socket
    import time
    
    client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
    client.connect(('127.0.0.1',9904))
    
    
    client.send('hello'.encode('utf-8'))
    time.sleep(5)
    client.send('world'.encode('utf-8'))
    

     服务端输出

    connect by  ('127.0.0.1', 10184)
    第一次 b'he'
    第二次 b'lloworld'
    

    解决粘包问题  

    问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是发送端在发送数据前,发一个头文件包,告诉发送的字节流总大小,然后接收端来一个死循环接收完所有数据

    使用struct模块可以用于将Python的值根据格式符,转换为字符串(byte类型)

    struct模块中最重要的三个函数是pack(), unpack(), calcsize()

    pack(fmt, v1, v2, ...)     按照给定的格式(fmt),把数据封装成字符串(实际上是类似于c结构体的字节流)

    unpack(fmt, string)       按照给定的格式(fmt)解析字节流string,返回解析出来的tuple

    calcsize(fmt)                 计算给定的格式(fmt)占用多少字节的内存

    struct中支持的格式如下表:

    FormatC TypePython字节数
    x pad byte no value 1
    c char string of length 1 1
    b signed char integer 1
    B unsigned char integer 1
    ? _Bool bool 1
    h short integer 2
    H unsigned short integer 2
    i int integer 4
    I unsigned int integer or long 4
    l long integer 4
    L unsigned long long 4
    q long long long 8
    Q unsigned long long long 8
    f float float 4
    d double float 8
    s char[] string 1

     使用案例

    import struct
    res = struct.pack('i',123)
    print(res,type(res), len(res)) # b'{x00x00x00' <class 'bytes'> 4 封装一个4个字节的包
    
    res1=struct.pack('q',11122232323)
    print(res1,type(res1), len(res1)) # b'x03xccxefx96x02x00x00x00' <class 'bytes'> 8 封装一个8个字节的包
    
    
    print(struct.unpack('i',res)[0])  # 拆包
    print(struct.unpack('q',res1)[0]) # 
    
    #输出
    # b'{x00x00x00' <class 'bytes'> 4
    # b'x03xccxefx96x02x00x00x00' <class 'bytes'> 8
    # (123,)
    # (11122232323,)
    

     

    解决粘包问题简单版(适用于传输字节较小)

    server

    import socket
    import subprocess
    import struct
    
    def cmd_exec(cmd):
        """
        执行shell命令
        :param cmd:
        :return:
        """
        p = subprocess.Popen(cmd, shell=True,
        stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    
        stdout, stderr = p.communicate()
        if p.returncode != 0:
            return stderr
        return stdout
    
    sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 重用地址端口
    sock_server.bind(('127.0.0.1', 8088))
    
    sock_server.listen(1)  # 开始监听,1代表在允许有一个连接排队,更多的新连接连进来时就会被拒绝
    print('starting...')
    while True:
        conn, client_addr = sock_server.accept()  # 阻塞直到有连接为止,有了一个新连接进来后,就会为这个请求生成一个连接对象
    
        print(client_addr)
    
        while True:
            try:
                data = conn.recv(1024) # 接收1024个字节
                if not data: break  # 适用于linux操作系统,防止客户端断开连接后死循环
                print('客户端的命令', data.decode('gbk'))
                res = cmd_exec(data.decode('gbk'))  # 执行cmd命令
                # 第一步:制作固定长度的报头4bytes
                total_size = len(res)
                header = struct.pack('i',total_size)
    
                #  第二步:把报头发送给客户端
                conn.send(header)
    
                # 第三步:再发送真实的数据
                conn.sendall(res)
    
            except ConnectionResetError:  # 适用于windows操作系统,防止客户端断开连接后死循环
                break
        conn.close()
    
    server.close()
    

    client

    import socket
    import struct
    
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print(client)
    
    
    client.connect(('127.0.0.1', 8088))
    
    while True:
        data = input('input >>>')
        if not data:  # 如果数据为空,继续输入
            continue
    
        client.send(data.encode('GBK'))  # 发送数据
    
        # 第一步:先收报头
        header = client.recv(4)
        # 第二步:从报头中解析出对真实数据的描述信息(数据的长度)
        total_size = struct.unpack('i',header)[0]
        print('收到数据长度=',total_size)
    
        # 第三步:接收真实的数据
        recv_size = 0
        recv_data = b''
        while recv_size < total_size:
            data = client.recv(1024)   # 接收数据
            recv_data += data
            recv_size += len(data)  # 不能加1024,如果加进度条,会计算有误
    
    
    
        print('接收数据 =', recv_data.decode('gbk', 'ignore'))  # 如果设置为ignore,则会忽略非法字符;
    
    client.close()  # 关闭
    

    输出结果:

    server端
    starting...
    ('127.0.0.1', 13338)
    客户端的命令 dir
    客户端的命令 ipconfig/all
    
    client端:
    "C:Program FilesPython36python.exe" "路飞/第三模块/第二章网络编程/01 简单的套接字通信/客户端.py"
    <socket.socket fd=216, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0>
    input >>>dir
    收到数据长度= 477
    接收数据 =  驱动器 C 中的卷是 BOOTCAMP
     卷的序列号是 D471-4F4D
    
     C:路飞第三模块第二章网络编程1 简单的套接字通信 的目录
    
    2018/07/07  14:02    <DIR>          .
    2018/07/07  14:02    <DIR>          ..
    2018/07/05  22:43               594 cmd_util.py
    2018/07/07  14:02               971 客户端.py
    2018/07/07  14:01             1,673 服务端.py
                   3 个文件          3,238 字节
                   2 个目录 28,749,410,304 可用字节
    
    input >>>ipconfig/all
    收到数据长度= 7702
    接收数据 = 
    Windows IP 配置
    
       主机名  . . . . . . . . . . . . . : PC
       主 DNS 后缀 . . . . . . . . . . . : 
       节点类型  . . . . . . . . . . . . : 混合
       IP 路由已启用 . . . . . . . . . . : 否
       WINS 代理已启用 . . . . . . . . . : 否
    
    以太网适配器 本地连接 3:
    
       媒体状态  . . . . . . . . . . . . : 媒体已断开
       连接特定的 DNS 后缀 . . . . . . . : 
       描述. . . . . . . . . . . . . . . : Bluetooth PAN Network Adapter
       物理地址. . . . . . . . . . . . . : 60-F8-1D-zz-89-EF
       DHCP 已启用 . . . . . . . . . . . : 是
       自动配置已启用. . . . . . . . . . : 是
    
    无线局域网适配器 无线网络连接:
    
       连接特定的 DNS 后缀 . . . . . . . : 
       描述. . . . . . . . . . . . . . . : Broadcom 802.11ac Network Adapter
       物理地址. . . . . . . . . . . . . : 60-F8-1D-AD-zz-EE
       DHCP 已启用 . . . . . . . . . . . : 是
       自动配置已启用. . . . . . . . . . : 是
       本地链接 IPv6 地址. . . . . . . . : fe80::55d1:e185:f929:8ce3%13(首选) 
       IPv4 地址 . . . . . . . . . . . . : 192.168.31.125(首选) 
       子网掩码  . . . . . . . . . . . . : 255.255.255.0
       获得租约的时间  . . . . . . . . . : 2018年7月7日 9:27:54
       租约过期的时间  . . . . . . . . . : 2018年7月8日 1:25:52
       默认网关. . . . . . . . . . . . . : 192.168.31.1
       DHCP 服务器 . . . . . . . . . . . : 192.168.31.1
       DHCPv6 IAID . . . . . . . . . . . : 291567645
       DHCPv6 客户端 DUID  . . . . . . . : 00-01-00-zz-7C-0D-6E-60-F8-1D-AD-89-EE
       DNS 服务器  . . . . . . . . . . . : 114.114.114.114
       TCPIP 上的 NetBIOS  . . . . . . . : 已启用
    
    以太网适配器 VirtualBox Host-Only Network:
    
       连接特定的 DNS 后缀 . . . . . . . : 
       描述. . . . . . . . . . . . . . . : VirtualBox Host-Only Ethernet Adapter
       物理地址. . . . . . . . . . . . . : 0A-00-27-00-zz-13
       DHCP 已启用 . . . . . . . . . . . : 否
       自动配置已启用. . . . . . . . . . : 是
       本地链接 IPv6 地址. . . . . . . . : fe80::7d26:2c96:84f1:6c4d%19(首选) 
       自动配置 IPv4 地址  . . . . . . . : 169.254.108.77(首选) 
       子网掩码  . . . . . . . . . . . . : 255.255.0.0
       默认网关. . . . . . . . . . . . . : 192.168.56.255
       DHCPv6 IAID . . . . . . . . . . . : 336199719
       DHCPv6 客户端 DUID  . . . . . . . : 00-01-00-01-21-7C-0zz60-F8-1D-AD-89-EE
       DNS 服务器  . . . . . . . . . . . : fec0:0:0:ffff::1%1
                                           fec0:0:0:ffff::2%1
                                           fec0:0:0:ffff::3%1
       TCPIP 上的 NetBIOS  . . . . . . . : 已启用
    
    以太网适配器 VirtualBox Host-Only Network #2:
    
       连接特定的 DNS 后缀 . . . . . . . : 
       描述. . . . . . . . . . . . . . . : VirtualBox Host-Only Ethernet Adapter #2
       物理地址. . . . . . . . . . . . . : 0A-00-27-00-00-14
       DHCP 已启用 . . . . . . . . . . . : 否
       自动配置已启用. . . . . . . . . . : 是
       本地链接 IPv6 地址. . . . . . . . : fe80::641c:b67e:fa43:a28d%20(首选) 
       IPv4 地址 . . . . . . . . . . . . : 192.168.96.1(首选) 
       子网掩码  . . . . . . . . . . . . : 255.255.255.0
       默认网关. . . . . . . . . . . . . : 
       DHCPv6 IAID . . . . . . . . . . . : 537526311
       DHCPv6 客户端 DUID  . . . . . . . : 00-01-00-01-21-7C-0D-6E-60-F8-1D-AD-89-EE
       DNS 服务器  . . . . . . . . . . . : fec0:0:0:ffff::1%1
                                           fec0:0:0:ffff::2%1
                                           fec0:0:0:ffff::3%1
       TCPIP 上的 NetBIOS  . . . . . . . : 已启用
    
    以太网适配器 VMware Network Adapter VMnet1:
    
       连接特定的 DNS 后缀 . . . . . . . : 
       描述. . . . . . . . . . . . . . . : VMware Virtual Ethernet Adapter for VMnet1
       物理地址. . . . . . . . . . . . . : 00-50-56-C0-00-01
       DHCP 已启用 . . . . . . . . . . . : 是
       自动配置已启用. . . . . . . . . . : 是
       本地链接 IPv6 地址. . . . . . . . : fe80::20c1:b2f0:8bff:626c%25(首选) 
       IPv4 地址 . . . . . . . . . . . . : 192.168.109.1(首选) 
       子网掩码  . . . . . . . . . . . . : 255.255.255.0
       获得租约的时间  . . . . . . . . . : 2018年7月7日 9:27:50
       租约过期的时间  . . . . . . . . . : 2018年7月7日 14:27:49
       默认网关. . . . . . . . . . . . . : 
       DHCP 服务器 . . . . . . . . . . . : 192.168.109.254
       DHCPv6 IAID . . . . . . . . . . . : 385896534
       DHCPv6 客户端 DUID  . . . . . . . : 00-01-00-01-21-7C-0D-6E-60-F8-1D-AD-89-EE
       DNS 服务器  . . . . . . . . . . . : fec0:0:0:ffff::1%1
                                           fec0:0:0:ffff::2%1
                                           fec0:0:0:ffff::3%1
       TCPIP 上的 NetBIOS  . . . . . . . : 已启用
    
    以太网适配器 VMware Network Adapter VMnet8:
    
       连接特定的 DNS 后缀 . . . . . . . : 
       描述. . . . . . . . . . . . . . . : VMware Virtual Ethernet Adapter for VMnet8
       物理地址. . . . . . . . . . . . . : 00-50-56zz-00-08
       DHCP 已启用 . . . . . . . . . . . : 是
       自动配置已启用. . . . . . . . . . : 是
       本地链接 IPv6 地址. . . . . . . . : fe80::61fd:5f66:1f70:cb3d%26(首选) 
       IPv4 地址 . . . . . . . . . . . . : 192.168.5.1(首选) 
       子网掩码  . . . . . . . . . . . . : 255.255.255.0
       获得租约的时间  . . . . . . . . . : 2018年7月7日 9:27:49
       租约过期的时间  . . . . . . . . . : 2018年7月7日 14:27:48
       默认网关. . . . . . . . . . . . . : 
       DHCP 服务器 . . . . . . . . . . . : 192.168.5.254
       DHCPv6 IAID . . . . . . . . . . . : 402673750
       DHCPv6 客户端 DUID  . . . . . . . : 00-01-00-01-21-7C-0D-6E-60-F8-1D-AD-89-EE
       DNS 服务器  . . . . . . . . . . . : fec0:0:0:ffff::1%1
                                           fec0:0:0:ffff::2%1
                                           fec0:0:0:ffff::3%1
       主 WINS 服务器  . . . . . . . . . : 192.168.5.2
       TCPIP 上的 NetBIOS  . . . . . . . : 已启用
    
    隧道适配器 本地连接* 14:
    
       媒体状态  . . . . . . . . . . . . : 媒体已断开
       连接特定的 DNS 后缀 . . . . . . . : 
       描述. . . . . . . . . . . . . . . : Microsoft ISATAP Adapter #2
       物理地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
       DHCP 已启用 . . . . . . . . . . . : 否
       自动配置已启用. . . . . . . . . . : 是
    
    隧道适配器 Teredo Tunneling Pseudo-Interface:
    
       媒体状态  . . . . . . . . . . . . : 媒体已断开
       连接特定的 DNS 后缀 . . . . . . . : 
       描述. . . . . . . . . . . . . . . : Teredo Tunneling Pseudo-Interface
       物理地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
       DHCP 已启用 . . . . . . . . . . . : 否
       自动配置已启用. . . . . . . . . . : 是
    
    隧道适配器 isatap.{0DA4A980-7247-4922-AAFB-55760B865C15}:
    
       媒体状态  . . . . . . . . . . . . : 媒体已断开
       连接特定的 DNS 后缀 . . . . . . . : 
       描述. . . . . . . . . . . . . . . : Microsoft ISATAP Adapter #3
       物理地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
       DHCP 已启用 . . . . . . . . . . . : 否
       自动配置已启用. . . . . . . . . . : 是
    
    隧道适配器 isatap.localdomain:
    
       媒体状态  . . . . . . . . . . . . : 媒体已断开
       连接特定的 DNS 后缀 . . . . . . . : 
       描述. . . . . . . . . . . . . . . : Microsoft ISATAP Adapter #5
       物理地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
       DHCP 已启用 . . . . . . . . . . . : 否
       自动配置已启用. . . . . . . . . . : 是
    
    隧道适配器 本地连接* 15:
    
       媒体状态  . . . . . . . . . . . . : 媒体已断开
       连接特定的 DNS 后缀 . . . . . . . : 
       描述. . . . . . . . . . . . . . . : Microsoft ISATAP Adapter #6
       物理地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
       DHCP 已启用 . . . . . . . . . . . : 否
       自动配置已启用. . . . . . . . . . : 是
    
    隧道适配器 isatap.{94C5F926-3E20-4589-A88E-54A36934D42C}:
    
       媒体状态  . . . . . . . . . . . . : 媒体已断开
       连接特定的 DNS 后缀 . . . . . . . : 
       描述. . . . . . . . . . . . . . . : Microsoft ISATAP Adapter #8
       物理地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
       DHCP 已启用 . . . . . . . . . . . : 否
       自动配置已启用. . . . . . . . . . : 是
    
    input >>>
    

      

    解决粘包问题优化版(适用于传输字节很大)

    server端

    import socket
    import subprocess
    import struct
    import json
    
    def cmd_exec(cmd):
        """
        执行shell命令
        :param cmd:
        :return:
        """
        p = subprocess.Popen(cmd, shell=True,
        stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    
        stdout, stderr = p.communicate()
        if p.returncode != 0:
            return stderr
        return stdout
    
    sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 重用地址端口
    sock_server.bind(('127.0.0.1', 8088))
    
    sock_server.listen(1)  # 开始监听,1代表在允许有一个连接排队,更多的新连接连进来时就会被拒绝
    print('starting...')
    while True:
        conn, client_addr = sock_server.accept()  # 阻塞直到有连接为止,有了一个新连接进来后,就会为这个请求生成一个连接对象
    
        print(client_addr)
    
        while True:
            try:
                data = conn.recv(1024) # 接收1024个字节
                if not data: break  # 适用于linux操作系统,防止客户端断开连接后死循环
                print('客户端的命令', data.decode('gbk'))
                res = cmd_exec(data.decode('gbk'))  # 执行cmd命令
                # 第一步:制作固定长度的报头dict
                header_dict ={
                    'filename':'文件名',
                    'md5':'md5值',
                    'total_size':len(res)
                }
    
                header_json = json.dumps(header_dict, ensure_ascii='False',indent=2)  # 序列化json
                print(header_json)
    
                header_bytes = header_json.encode('utf-8')
    
                header = struct.pack('i', len(header_bytes))
    
                #  第二步:把报头长度发送给客户端
                conn.send(header)
                #  第三步:把报头内容发送给客户端
                conn.send(header_bytes)
    
                # 第四步:再发送真实的数据
                conn.sendall(res)
    
            except ConnectionResetError:  # 适用于windows操作系统,防止客户端断开连接后死循环
                break
        conn.close()
    
    server.close()
    

     client端

    import socket
    import struct
    import json
    
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print(client)
    
    
    client.connect(('127.0.0.1', 8088))
    
    while True:
        data = input('input >>>')
        if not data:  # 如果数据为空,继续输入
            continue
    
        client.send(data.encode('GBK'))  # 发送数据
    
        # 第一步:先收报头
        header = client.recv(4)
        # 第二步:从报头中解析(header数据的长度)
        header_size = struct.unpack('i',header)[0]
        print('收到报头长度=', header_size)
    
        # 第三步:收到报头解析出对真实数据的描述信息
        header_json = client.recv(header_size)
        header_dict = json.loads(header_json)
        print('收到报头内容=',header_dict)
        total_size = header_dict['total_size']
    
    
        # 第三步:接收真实的数据
        recv_size = 0
        recv_data = b''
        while recv_size < total_size:
            data = client.recv(1024)   # 接收数据
            recv_data += data
            recv_size += len(data)  # 不能加1024,如果加进度条,会计算有误
    
    
    
        print('接收数据 =', recv_data.decode('gbk', 'ignore'))  # 如果设置为ignore,则会忽略非法字符;
    
    client.close()  # 关闭
    

     

    结果

    server端
    starting...
    ('127.0.0.1', 15685)
    客户端的命令 ls
    {
      "filename": "u6587u4ef6u540d",
      "md5": "md5u503c",
      "total_size": 61
    }
    客户端的命令 dir
    {
      "filename": "u6587u4ef6u540d",
      "md5": "md5u503c",
      "total_size": 477
    }
    
    client端
    <socket.socket fd=216, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0>
    input >>>ls
    收到报头长度= 80
    收到报头内容= {'filename': '文件名', 'md5': 'md5值', 'total_size': 61}
    接收数据 = 'ls' 不是内部或外部命令,也不是可运行的程序
    或批处理文件。
    
    input >>>dir
    收到报头长度= 81
    收到报头内容= {'filename': '文件名', 'md5': 'md5值', 'total_size': 477}
    接收数据 =  驱动器 C 中的卷是 BOOTCAMP
     卷的序列号是 D471-4F4D
    
      简单的套接字通信 的目录
    
    2018/07/07  14:51    <DIR>          .
    2018/07/07  14:51    <DIR>          ..
    2018/07/05  22:43               594 cmd_util.py
    2018/07/07  14:51             1,204 客户端.py
    2018/07/07  14:51             2,098 服务端.py
                   3 个文件          3,896 字节
                   2 个目录 28,694,999,040 可用字节
    
    input >>>ipconfig/all
    收到报头长度= 82
    收到报头内容= {'filename': '文件名', 'md5': 'md5值', 'total_size': 7702}
    接收数据 = 
    Windows IP 配置
    ……

      

     

     

      

      

     

  • 相关阅读:
    第二个周六——3.9
    女王节,很开心——3.8
    女生节——3.7
    尴尬的一批——3.6
    周二——3.5
    周一——3.4
    Java基本语法_循环练习系列(二)——万年历
    Java基本语法_循环练习系列(一)——模拟双色球
    《剩女郎》的艺术魅力
    剩女郎剧评
  • 原文地址:https://www.cnblogs.com/xiao-apple36/p/9276777.html
Copyright © 2011-2022 走看看