流式协议
流式协议会产生粘包问题

发送端可以是1k1k的发送数据,而接收端的应用程序可以2k2k的提走数据,当然也有可能一次提走3k或6k数据,或者一次只提走几个字节的数据,也就是说,应用程序所看到的数据是一个整体,或者说是一个流(stream),一条消息有多个字节对应程序是不可见的,因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因
所谓粘包问题主要是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的.
TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段.若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据
两种情况会发生粘包
发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据很小,会合到一起,产生粘包)
接收方不即使接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
解决方法:自定义应用层协议
为字节流加上自定义固定长度报头,包头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真是数据
struct模块:该模块可以把一个类型,如数字,转成固定长度的bytes

struct.pack('i',1111111111)
struct.error:'i' format requires -2147483648 <= number <= 2147483647 # 这个是范围 #
发送时
先发送报头长度
再编码报头内容然后发送
最后发送真实内容
接收时
先收报头长度,用struct提取出来
根据取出的长度收取报头内容,然后解码,反序列化
从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容
服务端
import subprocess
import struct
from socket import *
server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8080))
server.listen(5)
while True:
conn, client_addr = server.accept()
print(conn)
print(client_addr)
while True:
try:
cmd = conn.recv(1024)
obj = subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
stdout = obj.stdout.read()
stderr = obj.stdout.read()
total_size = len(stdout) + len(stderr)
# 先发送数据的长度
conn.send(struct.pack('i',total_size))
# 发送真正的数据
conn.send(stdout)
conn.send(stderr)
except Exception:
break
conn.close()
客户端
import struct
from socket import *
client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8082))
while True:
cmd = input(">>: ").strip()
if len(cmd) == 0:
continue
client.send(cmd.encode('utf-8'))
# 先收数据的长度
n = 0
header = b''
while n < 4:
data = client.recv(1)
header += data
n += len(data)
total_size = struct.unpack('i', header)[0]
# 收真正的数据
recv_size = 0
res = b''
while recv_size < total_size:
data = client.recv(1024)
res += data
recv_size += len(data)
print(res.decode('gbk'))
client.close()
网友评论