python粘包分析与解决
TCP与UDP协议
- TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
- UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
- 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中支持的格式如下表:
Format | C Type | Python | 字节数 |
---|---|---|---|
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'{\x00\x00\x00' <class 'bytes'> 4 封装一个4个字节的包
- res1=struct.pack('q',11122232323)
- print(res1,type(res1), len(res1)) # b'\x03\xcc\xef\x96\x02\x00\x00\x00' <class 'bytes'> 8 封装一个8个字节的包
- print(struct.unpack('i',res)[0]) # 拆包
- print(struct.unpack('q',res1)[0]) #
- #输出
- # b'{\x00\x00\x00' <class 'bytes'> 4
- # b'\x03\xcc\xef\x96\x02\x00\x00\x00' <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 Files\Python36\python.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:路飞\第三模块\第二章网络编程\01 简单的套接字通信 的目录
- 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": "\u6587\u4ef6\u540d",
- "md5": "md5\u503c",
- "total_size": 61
- }
- 客户端的命令 dir
- {
- "filename": "\u6587\u4ef6\u540d",
- "md5": "md5\u503c",
- "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 配置
……
python粘包分析与解决的更多相关文章
- python 粘包问题及解决方法
一粘包 TCP协议是面向对象的,面向流的,提高可靠性服务.使用了优化算法,Nagle算法.将多次间隔较少且数据量小的数据,合并成一个大的数据块,然后进行封包.这样接收端就很难分辨出来.TCP协议数据是 ...
- 网络编程——TCP协议、UDP协议、socket套接字、粘包问题以及解决方法
网络编程--TCP协议.UDP协议.socket套接字.粘包问题以及解决方法 TCP协议(流式协议) 当应用程序想通过TCP协议实现远程通信时,彼此之间必须先建立双向通信通道,基于该双向通道实现数 ...
- 网络编程----粘包以及粘包问题的解决、FTP上传
一.粘包现象 让我们基于tcp先制作一个远程执行命令的程序(1:执行错误命令 2:执行ls 3:执行ifconfig) 注意注意: res=subprocess.Popen(cmd.decode('u ...
- 什么是TCP粘包?怎么解决这个问题
在socket网络编程中,都是端到端通信,由客户端端口+服务端端口+客户端IP+服务端IP+传输协议组成的五元组可以明确的标识一条连接.在TCP的socket编程中,发送端和接收端都有成对的socke ...
- python之路--subprocess,粘包现象与解决办法,缓冲区
一. subprocess 的简单用法 import subprocess sub_obj = subprocess.Popen( 'dir', #系统指令 shell=True, #固定方法 std ...
- Python socket粘包问题(初级解决办法)
server端配置: import socket,subprocess,struct from socket import * server=socket(AF_INET,SOCK_STREAM) s ...
- python 之网络编程(基于TCP协议Socket通信的粘包问题及解决)
8.4 粘包问题 粘包问题发生的原因: 1.发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包),这样接收端,就难于分辨出来了,必须提供科学的拆包机制. ...
- Netty(三) 什么是 TCP 拆、粘包?如何解决?
前言 记得前段时间我们生产上的一个网关出现了故障. 这个网关逻辑非常简单,就是接收客户端的请求然后解析报文最后发送短信. 但这个请求并不是常见的 HTTP ,而是利用 Netty 自定义的协议. 有个 ...
- 什么是 TCP 拆、粘包?如何解决(Netty)
前言 记得前段时间我们生产上的一个网关出现了故障. 这个网关逻辑非常简单,就是接收客户端的请求然后解析报文最后发送短信. 但这个请求并不是常见的 HTTP ,而是利用 Netty 自定义的协议. 有个 ...
随机推荐
- oracle创建表空间、添加数据库文件
创建表空间: create [undo|TEMPORARY]tablespace venn datafile '/opt/oracle/db01/app/oracle/oradata/OSSORCL/ ...
- Le Chapitre X
Il se trouvait dans la région des astéroïdes 325, 326, 327, 328, 329 et 330. Il commença donc par le ...
- c#中委托与事件
参考:http://www.tracefact.net/tech/009.html 张子阳:http://www.cnblogs.com/JimmyZhang/archive/2007/09/23/9 ...
- SpringBoot2.0.2 Application调用的三种方式
一.注解 @SpringBootApplication 点开查看源码是由多个注解合成的注解,其中主要的注解有: @SpringBootConfigurati ...
- hadoop yarn组件介绍
Yarn的产生 mapReduc1.0 1单点故障 2扩展效率低 3资源利用率高 降低运维成本 方便数据共享 多计算框架支持 MapReduce Spark Storm Yarn的架构图 Yarn模块 ...
- 2.2.1synchronized方法的弊端
缺陷:用关键字synchronized声明方法是有弊端的,譬如A线程调用同步方法执行一个长时间的任务,那么B线程则必须等待较长的时间, 解决方法:使用synchronized同步语句块 package ...
- BAT的真的适合创业团队吗?
平时在公司扮演一个逗比得角色和亲爱的们友好相处的我根本不愿意去思考这么深入的课题.本来在上一家公司就涉及的太深,心爱的一条小产品线被咔掉后心疼不已.只想深入研究技术不问世事了.怎奈何突然有一天说要招一 ...
- Hdu2841 Visible Trees 2017-06-27 22:13 24人阅读 评论(0) 收藏
Visible Trees Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) To ...
- MyBatis高级篇之整合ehcache缓存框架
MyBatis高级篇之整合ehcache缓存框架 2017-09-01 0 Comments 1,671 Views 0 Times 一.前言 MyBatis为我们提供了Cache接口,也提供 ...
- MEAN 27
添加评论主题页,在26的基础上. 新建了很多文件 Error: Cannot find module './controllers/customers_c' 解决办法:../ 接下来做function ...