粘包问题
- 只有TCP有粘包现象,UDP永远不会粘包
socket收发消息的原理
在TCP协议中,如服务端向客户端传输数据:发送端是两K两K的发送数据,接收端是一K一K的发送数据,发送端将多余的的数据放在缓存中,接收端在取的时候会取到两条数据的部分。造成粘包问题
在UDP协议中,服务端向客户端传输数据:如发送端发送1025K的数据,而接收端只能接收1024K的数据,那多余的数据就会直接丢掉,下一条数据同理。所以UDP协议是不可靠传输,适用于传输小数据。不会有粘包问题
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
TCP发送数据的四种情况
假设客户端分别发送了两个数据包D1和D2给服务端,由于服务端一次读取到的字节数是不确定的,故可能存在以下4种情况。
- 服务端分两次读取到了两个独立的数据包,分别是D1和D2,没有粘包和拆包;
- 服务端一次接收到了两个数据包,D1和D2粘合在一起,被称为TCP粘包;
- 服务端分两次读取到了两个数据包,第一次读取到了完整的D1包和D2包的部分内容,第二次读取到了D2包的剩余内容,这被称为TCP拆包;
- 服务端分两次读取到了两个数据包,第一次读取到了D1包的部分内容D1_1,第二次读取到了D1包的剩余内容D1_2和D2包的整包。
粘包情况:两个数据非常小,然后间隔时间又短;数据太大,一次性取不完,下一次还会取这个大数据
解决粘包问题
解决粘包问题的原理:在传输数据之前,数据的大小必须得定长
服务端
import subprocess
import struct
from socket import *
server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8000))
server.listen(5)
print('start...')
# 连接循环
while True:
conn, client_addr = server.accept()
# 通信循环
while True:
try:
cmd = conn.recv(1024)
print('来自客户端信息:', cmd)
pipeline = subprocess.Popen(cmd.decode('utf8'),
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)
stdout = pipeline.stdout.read()
stderr = pipeline.stdout.read()
byte_len = len(stdout) + len(stderr)
guding_bytes = struct.pack('i', byte_len)
conn.send(guding_bytes)
conn.send(stdout+ stderr)
except ConnectionResetError:
break
客户端
import struct
from socket import *
client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8000))
while True:
cmd = input('please enter cmd>>>')
client.send(cmd.encode('utf8'))
guding_bytes = client.recv(4)
data_len = struct.unpack('i', guding_bytes)[0]
data = client.recv(data_len)
print(data.decode('gbk'))
为何TCP是可靠传输,UDP是不可靠传输
- TCP在数据传输时,发送端先把数据发送到自己的缓存中,然后协议控制将缓存中的数据发往对端,对端返回一个ack=1,发送端则清理缓存中的数据,对端返回ack=0,则重新发送数据,所以TCP是可靠的
- udp发送数据,对端是不会返回确认信息的,因此不可靠