一.什么是粘包:
TCP是可靠的面向连接的协议,传输效率低,全双工通信,面向字节流.
UDP是不可靠的无连接的协议,传输效率高,无阻塞控制.
粘包的成因是由于TCP协议本身造成的,TCP为了提高传输效率,发送方往往会收集到足够多的数据才发送一个TCP段.如果连续几次发送的数据都很少,TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样,接收方接收到的数据就发生了粘包.
发生粘包的两种情况:
1.由于发送端的缓存机制导致的粘包
发送端需要等缓存区满以后才发送,造成粘包(发送数据时间间隔短,数据少,就会合到一起,产生粘包.)
import socket server = socket.socket() ip_port = ('127.0.0.1', 8000) server.bind(ip_port) server.listen(5) conn,addr = server.accept() data1 = conn.recv(10) data2 = conn.recv(10) print('data1', data1.decode('utf-8')) print('data2', data2.decode('utf-8')) conn.close()
import socket client = socket.socket() ip_port = ('127.0.0.1', 8000) client.connect(ip_port) client.send('hello'.encode('utf-8')) client.send('world'.encode('utf-8'))
此时服务端收到的消息:(data1:helloworld),(data2: )
2.由于服务端的缓存机制导致的粘包
接收方没有及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只接受了一部分,服务端下次接收数据的时候还是从缓存区上次剩下的地方开始拿,产生了粘包.)
import socket server = socket.socket() ip_port = ('127.0.0.1', 8000) server.bind(ip_port) server.listen(5) conn,addr = server.accept() data1 = conn.recv(2) data2 = conn.recv(10) print('data1', data1.decode('utf-8')) print('data2', data2.decode('utf-8')) conn.close()
import socket client = socket.socket() ip_port = ('127.0.0.1', 8000) client.connect(ip_port) client.send('hello'.encode('utf-8')) client.send('world'.encode('utf-8'))
此时服务端收到的消息:(data1:he), (data2:lloworld)
总结:
1.粘包只发生在TCP协议中.
2.粘包不一定会发生
3.表面上看,粘包问题主要是因为发送方和接收方分缓存机制,TCP面向连接通信的特点产生的.
4.实际上,主要因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据造成的.
粘包的解决方案
import socket import subprocess import struct #struct 可以定制报头 server = socket.socket() ip_port = ('127.0.0.1', 8018) server.bind(ip_port) server.listen(5) while True: print('server is working......') conn,addr = server.accept() while True: try: #处理客户端断开连接异常 cmd = conn.recv(1024) if cmd == b'exit': break res = subprocess.Popen(cmd.decode('utf-8'), shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, ) err = res.stderr.read() if err: data = err else: data = res.stdout.read() conn.send(struct.pack('i', len(data))) #自定制报头 conn.send(data) #发送所有报文 except Exception as e: break conn.close()
import socket import struct client = socket.socket() ip_port = ('127.0.0.1', 8018) client.connect(ip_port) while True: cmd = input('请输入命令:') if cmd == '': continue elif cmd == 'exit': break client.send(cmd.encode('utf-8')) header_pack = client.recv(4) header = struct.unpack('i', header_pack)[0] data_length = 0 data_num = b'' while data_length < header: data = client.recv(1024) data_num += data data_length += len(data) print(data_num.decode('gbk'))