文章目录
一、socket粘包问题
-
什么是粘包:粘包指的是数据和数据之间没有明确的分界线,导致不能正确读取数据
应用程序无法直接操作硬件,应用程序想要发送数据则必须将数据交给操作系统,而操作系统需要需要同时为所有应用程序提供数据传输服务,也就意味着,操作系统不可能立马就能将应用程序的数据发送出去,就需要为应用程序提供一个缓冲区,用于临时存放数据,具体流程如下:
反送方:
当应用程序调用send函数时,应用程序会将数据从应用程序拷贝到操作系统缓存,再由操作系统从缓冲区读取数据并发送出去
接收方:
对方计算机收到数据也是操作系统先收到,至于应用程序何时处理这些数据,操作系统并不清楚,所以同样需要将数据先存储到操作系统的缓冲区中,当应用程序调用recv时,实际上是从操作系统缓冲区中将数据拷贝到应用程序的过程
上述过程对于TCP与UDP都是相同的不同之处在于:
UDP:
UDP在收发数据时是基于数据包的,即一个包一个包的发送,包与包之间有着明确的分界,到达对方操作系统缓冲区后也是一个一个独立的数据包,接收方从操作系统缓冲区中将数据包拷贝到应用程序
这种方式存在的问题:
- 发送方发送的数据长度每个操作系统会有不同的限制,数据超过限制则无法发送
- 接收方接收数据时如果应用程序的提供的缓存容量小于数据包的长度将造成数据丢失,而缓冲区大小不可能无限大
TCP:
当我们需要传输较大的数据,或需要保证数据完整性时,最简单的方式就是使用TCP协议了,与UDP不同的是,TCP增加了一套校验规则来保证数据的完整性,会将超过TCP包最大长度的数据拆分为多个TCP包,并在传输数据时为每一个TCP数据包指定一个顺序号,接收方在收到TCP数据包后按照顺序将数据包进行重组,重组后的数据全都是二进制数据,且每次收到的二进制数据之间没有明显的分界
基于这种工作机制TCP在三种情况下会发送粘包问题
- 当单个数据包较小时接收方可能一次性读取了多个包的数据
- 当整体数据较大时接收方可能一次仅读取了一个包的一部分内容
- 另外TCP协议为了提高效率,增加了一种优化机制,会将数据较小且发送间隔较短的数据合并发送,该机制也会导致发送方将两个数据包粘在一起发送
基础解决方案:
首先明确只有TCP会出现粘包问题,之所以粘包是因为接收方不知道一次该接收的数据长度,那如何才能让接收方知道数据的长度呢?
解决方案:在发送数据前先发送数据长度
cmd 服务端:
import socket
import subprocess
import struct
server = socket.socket()
server.bind(("127.0.0.1",9090))
server.listen()
while True:
client,addr = server.accept()
while True:
try:
#接收客户端命令
cmd = client.recv(1024).decode("utf-8")
p = subprocess.Popen(cmd,shell=True,stdout=-1,stderr=-1)
# data与err_data都是采用的系统编码,windows是GBK
data = p.stdout.read()
err_data = p.stderr.read()
print("数据长度:%s" % (len(data) + len(err_data)))
#计算数据长度
length = len(data) + len(err_data)
#将int类型的长度转成字节
len_data = struct.pack("i",length)
# 先发送长度,在发真实数据有可能长度数据和真实数据黏在一起,而接收方不知道长度数据的字节数 导致黏包
# 解决的方案就是 长度信息占的字节数固定死 整数 转成一个固定长度字节
# 先发送长度给客户端
client.send(len_data)
# 再发送数据给客户端
client.send