在看粘包之前我们先看一个实例
这个实例是在客户端输入指令在服务端执行并返回执行结果
其中subprocess就是将命令交予系统进行运行的模块
代码
- 服务端
import socket
import subprocess
ip_port=('127.0.0.1',8001)
backlog=5
buffersize=1024
tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_server.bind(ip_port)
tcp_server.listen(backlog)
while True:
conn,addr = tcp_server.accept()
while True:
cmd = conn.recv(buffersize).decode('utf-8')
print(cmd)
if cmd == 'exit':
break
res = subprocess.Popen(cmd,shell=True,stderr=subprocess.PIPE,stdout=subprocess.PIPE,stdin=subprocess.PIPE)
#表示使用shell来解释语言,标准输入,标准输出和错误输入都输入到管道,而不是打印到屏幕上
err = res.stderr.read()
if err:
cmd_res = err
else:
cmd_res = res.stdout.read()
conn.send(cmd_res)
conn.close()
- 客户端
import socket
buffersize=1024
ip_port = ('127.0.0.1',8001)
tcp_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_client.connect(('127.0.0.1',8001))
while True:
msg = input('-->:')
if not msg:continue
tcp_client.send(msg.encode('utf-8'))
if msg == 'exit':
break
res = tcp_client.recv(buffersize).decode('utf-8')
print(res)
tcp_client.close()
当我们执行一个命令,它的返回值大于1024字节的时候,就会被截断,而当你执行下一个命令的时候,上次没有展示完的内容会继续输出到屏幕上,看起来就像两次的命令结果粘连到了一起,这就是粘包现象。
产生的原因
首先发送数据和接收数据不是一个实时的过程,发送数据都会首先发送至对方的内核态缓存区中,这个我们前面介绍过。发送方可以一次性发送2048字节的数据,而接收方可以一次收取1024字节数据,分两次接收,这是由buffersize决定的。也就是说应用程序对一段数据有多少字节,对它来说是不可见的,它只负责从流中取走一段,因此粘包现象就产生了。
两次发送消息的大小相加比如是2000字节(1300+700)形成一段流,第一次收取了1024没收取完;第二段收取了第一次消息的200加后一段的700。
而udp就不不同了,udp是面向数据报消息的边界都是标记好的,收和发都是以消息/次为单位,因此不会出现粘包的现象。
解决方法
基础解决的思路
产生粘包的主要原因就是tcp不知道每次消息的边界,因此只要我们每次发送消息时,提前先发送的消息的长度,然后根据消息的长度来接收消息。
- 服务端
import socket
import subprocess
ip_port=('127.0.0.1',8001)
backlog=5
buffersize=1024
tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_server.bind(ip_port)
tcp_server.listen(backlog)
while True:
conn,addr = tcp_server.accept()
while True:
cmd = conn.recv(buffersize).decode('utf-8')
print(cmd)
if cmd == 'exit':
break
res = subprocess.Popen(cmd,shell=True,stderr=subprocess.PIPE,stdout=subprocess.PIPE,stdin=subprocess.PIPE)
err = res.stderr.read()
if err:
cmd_res = err
else:
cmd_res = res.stdout.read()
##解决粘包
length = len(cmd_res)
conn.send(length)
client_ready = conn.recv(buffer_size)
if client_ready == b'ready':
conn.send(cmd_res)
conn.close()
- 客户端
import socket
buffersize=1024
ip_port = ('127.0.0.1',8001)
tcp_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_client.connect(('127.0.0.1',8001))
while True:
msg = input('-->:')
if not msg:continue
tcp_client.send(msg.encode('utf-8'))
if msg == 'exit':
break
###解决粘包
length = tcp_client.recv(buffersize)
###由于命令长度和命令内容发送间隔近,防止粘连,客户端发送‘ready’字样
tcp_client.send(b'ready')
#size统计接收的长度,msg是接收内容的变量,这是收大长度的内容的方法
recv_size = 0
recv_msg = b''
while recv_size < length:
recv_msg += tcp_client.recv(buffer_size)
recv_size = len(recv_msg)
res = recv_msg.decode('utf-8')
print(res)
tcp_client.close()