1、粘包
粘包,就是指两次结果粘到一起了。它的发生主要是因为socket缓冲区导致的,来看一下
你的程序实际上无权直接操作网卡的,你操作网卡都是通过操作系统给用户程序暴露出来的接口, 那每次你的程序要给远程发数据时,其实是先把数据从用户态copy到内核态, 这样的操作是耗资源和时间的,频繁的在内核态和用户态之前交换数据势必会导致发送效率降低, 因此socket 为提高传输效率,发送方往往要收集到足够多的数据后才发送一次数据给对方。 若连续几次需要send的数据都很少, 通常TCP socket 会根据优化算法把这些数据合成一个TCP段后一次发送出去, 这样接收方就收到了粘包数据。
2、粘包问题只存在于TCP中,Not UDP
- TCP协议是面向流的协议
还是看上图,发送端可以是一K一K地发送数据, 而接收端的应用程序可以两K两K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据, 也就是说,应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的, 因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因
- UDP是面向消息的协议
UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据, 这一点和TCP是很不同的。怎样定义消息呢?可以认为对方一次性write/send的数据为一个消息,需要明白的是当对方send一条信息的时候, 无论底层怎样分段分片,TCP协议层会把构成整条消息的数据段排序完成后才呈现在内核缓冲区。
总结
- TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
- UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
- tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头,实验略
3、send与recv的区别
4、粘包现象
- 两次send:数据量小,时间间隔很短,会发生粘包
(1)服务端
# 两次send:数据量小,时间间隔很短,会发生粘包 import socket import time server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('127.0.0.1', 9999)) server.listen(5) print('... waiting...') conn, addr = server.accept() #data1 = conn.recv(1024) data1 = conn.recv(1) # 当只取一个字符的时候,剩下的数据还在缓存池里面,下次会继续取出来 print('第一次', data1) data2 = conn.recv(1024) print('第二次', data2) conn.close() server.close()
(2)客户端
# 两次send:数据量小,时间间隔很短,会发生粘包 import socket import time client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(('127.0.0.1', 9999)) client.send('hello'.encode('utf-8')) # time.sleep(1) 两次send直接隔一段时间,不会发生粘包现象 client.send('world'.encode('utf-8')) client.close()
5、解决粘包问题:普通版
问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,
然后接收端来一个死循环接收完所有数据
(1)服务端
import socket import subprocess server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('127.0.0.1', 9994)) server.listen(5) while True: print('waiting...') conn, addr = server.accept() while True: try: data = conn.recv(1024) print('=》', data) if not data: break obj = subprocess.Popen(data.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) # 3.把取出命令的结果 stdout = obj.stdout.read() stderr = obj.stderr.read() # 3.1 把数据的长度发送给客户端 total_size = len(stdout+stderr) conn.send(str(total_size).encode('utf-8')) # 3.2 在发送真实的数据 conn.send(stdout) # 粘包可以 conn.send(stderr) except ConnectionResetError: break conn.close() server.close()
(2)客户端
import socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(('127.0.0.1', 9994)) while True: data = input('>>>').strip() # 粘包现象,当输入ipconfig的时候,缓存池会存放太多的结果 if not data: continue client.send(data.encode('utf-8')) # 2.接受数据的长度 total_size = client.recv(1024) # 3.取出报文的长度 total_size = int(total_size.decode('utf-8')) # 3.一段段的取数据 total_data = b'' recv_size = 0 while recv_size < total_size: # 接受的数据长度 = len(total_data) 已经取完了,就退出 data = client.recv(1024) total_data += data recv_size += len(data) print(total_data.decode('gbk')) client.close()
C:Python34python.exe C:/PycharmProjects/Luffy_project/Luffy-网络编程/07-解决粘包问题/03-客户端.py >>>dir 驱动器 C 中的卷没有标签。 卷的序列号是 E08A-FCF5 C:PycharmProjectsLuffy_projectLuffy-网络编程 7-解决粘包问题 的目录 2018/03/28 周三 下午 08:27 <DIR> . 2018/03/28 周三 下午 08:27 <DIR> .. 2018/03/28 周三 下午 08:10 1,114 02-服务端2.py 2018/03/28 周三 下午 08:27 818 03-客户端.py 2 个文件 1,932 字节 2 个目录 124,168,138,752 可用字节 >>> >>>ipconfig Windows IP 配置 无线局域网适配器 无线网络连接 5: 连接特定的 DNS 后缀 . . . . . . . : 本地链接 IPv6 地址. . . . . . . . : fe80::7512:dae3:9ef5:f6d3%19 IPv4 地址 . . . . . . . . . . . . : 192.168.191.1 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : 无线局域网适配器 无线网络连接: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 以太网适配器 本地连接: 连接特定的 DNS 后缀 . . . . . . . : IPv6 地址 . . . . . . . . . . . . : 2001:da8:4015:a:2060:b9c5:9ddb:1343 临时 IPv6 地址. . . . . . . . . . : 2001:da8:4015:a:65c2:fdc:ae01:f622 本地链接 IPv6 地址. . . . . . . . : fe80::2060:b9c5:9ddb:1343%12 IPv4 地址 . . . . . . . . . . . . : 10.8.6.11 子网掩码 . . . . . . . . . . . . : 255.255.254.0 默认网关. . . . . . . . . . . . . : fe80::3ee5:a6ff:fe6c:98c1%12 10.8.7.254 以太网适配器 VMware Network Adapter VMnet1: 连接特定的 DNS 后缀 . . . . . . . : 本地链接 IPv6 地址. . . . . . . . : fe80::91c:c41:5755:bd67%14 IPv4 地址 . . . . . . . . . . . . : 192.168.48.1 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : 以太网适配器 VMware Network Adapter VMnet8: 连接特定的 DNS 后缀 . . . . . . . : 本地链接 IPv6 地址. . . . . . . . : fe80::4cfa:7057:d006:cbf0%15 IPv4 地址 . . . . . . . . . . . . : 192.168.28.1 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : 隧道适配器 isatap.{D0793028-C9C4-44BD-84A9-7ACFE0D6D454}: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 本地连接*: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 isatap.{592418B3-F47A-489C-A9E5-1EDEC7B499C1}: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 isatap.{D8C47ABB-48E9-4B35-BE81-29A998780E04}: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 isatap.{F30B37DD-15CE-4B19-8B0F-BF1495C3081D}: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 isatap.{1A8AE88C-5BF7-4219-B60F-72E3ACF6FF35}: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 6TO4 Adapter: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : >>> >>>dir 驱动器 C 中的卷没有标签。 卷的序列号是 E08A-FCF5 C:PycharmProjectsLuffy_projectLuffy-网络编程 7-解决粘包问题 的目录 2018/03/28 周三 下午 08:27 <DIR> . 2018/03/28 周三 下午 08:27 <DIR> .. 2018/03/28 周三 下午 08:10 1,114 02-服务端2.py 2018/03/28 周三 下午 08:27 818 03-客户端.py 2 个文件 1,932 字节 2 个目录 124,168,138,752 可用字节 >>>
6、解决粘包问题:struct模块
- 为字节流加上自定义固定长度报头也可以借助于第三方模块struc
import struct # 为字节流加上自定义固定长度报头 4个 bytes data = 'alex123' ret = struct.pack('i', len(data)) # i 模式 print(ret,ret.decode('utf-8'), len(ret), type(ret)) # b'x07x00x00x00' 4 <class 'bytes'> obj = struct.unpack('i', ret) print(obj) # (10,) data_len = obj[0] print(data_len) # 数据长度 7 个
(1)服务端
import socket import struct import subprocess server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('127.0.0.1', 9994)) server.listen(5) while True: print('waiting...') conn, addr = server.accept() while True: try: data = conn.recv(1024) print('=》', data) if not data: break obj = subprocess.Popen(data.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) # 3.把取出命令的结果 stdout = obj.stdout.read() stderr = obj.stderr.read() # 3.1 把数据的长度发送给客户端 # 第一步:制作固定长度的报头 total_size = len(stdout + stderr) ret = struct.pack('i',total_size) # bytes格式 # 第二部:把报头发送给客户端 conn.send(ret) # 第三部:发送真实数据 conn.send(stdout) # 粘包可以 conn.send(stderr) except ConnectionResetError: break conn.close() server.close()
(2)客户端
import socket import struct client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(('127.0.0.1', 9994)) while True: data = input('>>>').strip() # 粘包现象,当输入ipconfig的时候,缓存池会存放太多的结果 if not data: continue client.send(data.encode('utf-8')) # 2.接受报头 ret = client.recv(1024) # 3.从报头中解析出对真实数据的描述信息(数据的长度) obj = struct.unpack('i',ret) total_size = obj[0] # 3.一段段的取数据 total_data = b'' recv_size = 0 while recv_size < total_size: # 接受的数据长度 = len(total_data) 已经取完了,就退出 data = client.recv(1024) total_data += data recv_size += len(data) print(total_data.decode('gbk')) client.close()
C:Python34python.exe C:/PycharmProjects/Luffy_project/Luffy-网络编程/08-粘包-struct模块/03-客户端.py >>>ipconfig Windows IP 配置 无线局域网适配器 无线网络连接 5: 连接特定的 DNS 后缀 . . . . . . . : 本地链接 IPv6 地址. . . . . . . . : fe80::7512:dae3:9ef5:f6d3%19 IPv4 地址 . . . . . . . . . . . . : 192.168.191.1 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : 无线局域网适配器 无线网络连接: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 以太网适配器 本地连接: 连接特定的 DNS 后缀 . . . . . . . : IPv6 地址 . . . . . . . . . . . . : 2001:da8:4015:a:2060:b9c5:9ddb:1343 临时 IPv6 地址. . . . . . . . . . : 2001:da8:4015:a:65c2:fdc:ae01:f622 本地链接 IPv6 地址. . . . . . . . : fe80::2060:b9c5:9ddb:1343%12 IPv4 地址 . . . . . . . . . . . . : 10.8.6.11 子网掩码 . . . . . . . . . . . . : 255.255.254.0 默认网关. . . . . . . . . . . . . : fe80::3ee5:a6ff:fe6c:98c1%12 10.8.7.254 以太网适配器 VMware Network Adapter VMnet1: 连接特定的 DNS 后缀 . . . . . . . : 本地链接 IPv6 地址. . . . . . . . : fe80::91c:c41:5755:bd67%14 IPv4 地址 . . . . . . . . . . . . : 192.168.48.1 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : 以太网适配器 VMware Network Adapter VMnet8: 连接特定的 DNS 后缀 . . . . . . . : 本地链接 IPv6 地址. . . . . . . . : fe80::4cfa:7057:d006:cbf0%15 IPv4 地址 . . . . . . . . . . . . : 192.168.28.1 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : 隧道适配器 isatap.{D0793028-C9C4-44BD-84A9-7ACFE0D6D454}: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 本地连接*: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 isatap.{592418B3-F47A-489C-A9E5-1EDEC7B499C1}: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 isatap.{D8C47ABB-48E9-4B35-BE81-29A998780E04}: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 isatap.{F30B37DD-15CE-4B19-8B0F-BF1495C3081D}: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 isatap.{1A8AE88C-5BF7-4219-B60F-72E3ACF6FF35}: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 6TO4 Adapter: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : >>>dir 驱动器 C 中的卷没有标签。 卷的序列号是 E08A-FCF5 C:PycharmProjectsLuffy_projectLuffy-网络编程 8-粘包-struct模块 的目录 2018/03/28 周三 下午 10:30 <DIR> . 2018/03/28 周三 下午 10:30 <DIR> .. 2018/03/28 周三 下午 09:09 360 01-struct.py 2018/03/28 周三 下午 09:10 1,281 02-服务端2.py 2018/03/28 周三 下午 09:13 877 03-客户端.py 2018/03/28 周三 下午 09:15 436 04-struct超出.py 2018/03/28 周三 下午 09:39 902 05-标准数据报头格式.py 2018/03/28 周三 下午 10:30 1,738 06-服务端2.py 2018/03/28 周三 下午 10:30 1,222 07-客户端.py 7 个文件 6,816 字节 2 个目录 124,167,741,440 可用字节 >>>
7、数据长度超出struct的标准
8、制作标准报头
9、解决粘包问题:json struct
import json,struct #假设通过客户端上传1T:1073741824000的文件a.txt #为避免粘包,必须自定制报头 header={'file_size':1073741824000,'file_name':'/a/b/c/d/e/a.txt','md5':'8f6fbf8347faa4924a76856701edb0f3'} #1T数据,文件路径和md5值 #为了该报头能传送,需要序列化并且转为bytes head_bytes=bytes(json.dumps(header),encoding='utf-8') #序列化并转成bytes,用于传输 #为了让客户端知道报头的长度,用struck将报头长度这个数字转成固定长度:4个字节 head_len_bytes=struct.pack('i',len(head_bytes)) #这4个字节里只包含了一个数字,该数字是报头的长度 #客户端开始发送 conn.send(head_len_bytes) #先发报头的长度,4个bytes conn.send(head_bytes) #再发报头的字节格式 conn.sendall(文件内容) #然后发真实内容的字节格式 #服务端开始接收 head_len_bytes=s.recv(4) #先收报头4个bytes,得到报头长度的字节格式 x=struct.unpack('i',head_len_bytes)[0] #提取报头的长度 head_bytes=s.recv(x) #按照报头长度x,收取报头的bytes格式 header=json.loads(json.dumps(header)) #提取报头 #最后根据报头的内容提取真实的数据,比如 real_data_len=s.recv(header['file_size']) s.recv(real_data_len)
(1)服务端
import socket import struct import subprocess import json server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('127.0.0.1', 9994)) server.listen(5) while True: print('waiting...') conn, addr = server.accept() while True: try: data = conn.recv(1024) print('=》', data) if not data: break obj = subprocess.Popen(data.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) # 3.把取出命令的结果 stdout = obj.stdout.read() stderr = obj.stderr.read() # 3.1 把数据的长度发送给客户端 # 第一步:制作报头 total_size = len(stdout + stderr) header_dict = { 'filename': 'a.txt', 'md5': 'afdsafsaf', 'total_size': total_size } header_json = json.dumps(header_dict) # 报头的json样式 header_bytes = header_json.encode('utf-8') # 报头---报头.json---报头.bytes # 发送报头的长度 header_len = struct.pack('i', len(header_bytes)) # 报头bytes---> len()---> struct conn.send(header_len) # 第二部:把报头发送给客户端 conn.send(header_bytes) # 第三部:发送真实数据 conn.send(stdout) # 粘包可以 conn.send(stderr) except ConnectionResetError: break conn.close() server.close()
(2)客户端
import socket import struct import json client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(('127.0.0.1', 9994)) while True: data = input('>>>').strip() # 粘包现象,当输入ipconfig的时候,缓存池会存放太多的结果 if not data: continue client.send(data.encode('utf-8')) # 2.接受报头的长度 4 个bytes----》struct----》 header_bytes的len header_bytes_len = client.recv(1024) header_bytes_len = struct.unpack('i', header_bytes_len)[0] # 接收报头的json数据 header_bytes = client.recv(header_bytes_len) print(header_bytes) # 3.从报头中解析出对真实数据的描述信息(数据的长度) header_json = header_bytes.decode('utf-8') header_dict = json.loads(header_json) total_size = header_dict['total_size'] # 3.一段段的取数据 total_data = b'' recv_size = 0 while recv_size < total_size: # 接受的数据长度 = len(total_data) 已经取完了,就退出 data = client.recv(1024) total_data += data recv_size += len(data) print(total_data.decode('gbk')) client.close()
C:Python34python.exe C:/PycharmProjects/Luffy_project/Luffy-网络编程/08-粘包-struct模块/07-客户端.py >>>ipconfig b'{"total_size": 2587, "md5": "afdsafsaf", "filename": "a.txt"}' Windows IP 配置 无线局域网适配器 无线网络连接 5: 连接特定的 DNS 后缀 . . . . . . . : 本地链接 IPv6 地址. . . . . . . . : fe80::7512:dae3:9ef5:f6d3%19 IPv4 地址 . . . . . . . . . . . . : 192.168.191.1 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : 无线局域网适配器 无线网络连接: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 以太网适配器 本地连接: 连接特定的 DNS 后缀 . . . . . . . : IPv6 地址 . . . . . . . . . . . . : 2001:da8:4015:a:2060:b9c5:9ddb:1343 临时 IPv6 地址. . . . . . . . . . : 2001:da8:4015:a:65c2:fdc:ae01:f622 本地链接 IPv6 地址. . . . . . . . : fe80::2060:b9c5:9ddb:1343%12 IPv4 地址 . . . . . . . . . . . . : 10.8.6.11 子网掩码 . . . . . . . . . . . . : 255.255.254.0 默认网关. . . . . . . . . . . . . : fe80::3ee5:a6ff:fe6c:98c1%12 10.8.7.254 以太网适配器 VMware Network Adapter VMnet1: 连接特定的 DNS 后缀 . . . . . . . : 本地链接 IPv6 地址. . . . . . . . : fe80::91c:c41:5755:bd67%14 IPv4 地址 . . . . . . . . . . . . : 192.168.48.1 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : 以太网适配器 VMware Network Adapter VMnet8: 连接特定的 DNS 后缀 . . . . . . . : 本地链接 IPv6 地址. . . . . . . . : fe80::4cfa:7057:d006:cbf0%15 IPv4 地址 . . . . . . . . . . . . : 192.168.28.1 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : 隧道适配器 isatap.{D0793028-C9C4-44BD-84A9-7ACFE0D6D454}: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 本地连接*: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 isatap.{592418B3-F47A-489C-A9E5-1EDEC7B499C1}: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 isatap.{D8C47ABB-48E9-4B35-BE81-29A998780E04}: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 isatap.{F30B37DD-15CE-4B19-8B0F-BF1495C3081D}: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 isatap.{1A8AE88C-5BF7-4219-B60F-72E3ACF6FF35}: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 6TO4 Adapter: 媒体状态 . . . . . . . . . . . . : 媒体已断开 连接特定的 DNS 后缀 . . . . . . . : >>>dir b'{"total_size": 763, "md5": "afdsafsaf", "filename": "a.txt"}' 驱动器 C 中的卷没有标签。 卷的序列号是 E08A-FCF5 C:PycharmProjectsLuffy_projectLuffy-网络编程 8-粘包-struct模块 的目录 2018/03/30 周五 下午 11:52 <DIR> . 2018/03/30 周五 下午 11:52 <DIR> .. 2018/03/28 周三 下午 09:09 360 01-struct.py 2018/03/28 周三 下午 09:10 1,281 02-服务端2.py 2018/03/28 周三 下午 09:13 877 03-客户端.py 2018/03/30 周五 下午 11:52 436 04-struct超出.py 2018/03/28 周三 下午 09:39 902 05-标准数据报头格式.py 2018/03/28 周三 下午 10:30 1,738 06-服务端2.py 2018/03/28 周三 下午 10:30 1,222 07-客户端.py 7 个文件 6,816 字节 2 个目录 124,165,222,400 可用字节 >>>
10