黏包问题
概念
黏包问题只存在于基于TCP协议的Socket通信过程中,其是指发送者将多个数据包一起发送给接收者,造成接收者不能区分每个数据包的分界。
原因
发送者因素
基于TCP协议进行通信时,TCP协议默认会使用Nagle算法。Nagle算法是以减少数据包发送量来增进TCP/IP网络的性能。其工作方式是合并一定数量的输出数据后一次提交。特别的是,只要有已提交的数据包尚未确认,发送者会持续缓冲数据包,直到累积一定数量的数据才提交。
所以,基于TCP协议的Nagle算法,就引发了发送者所导致的黏包现象。
接收者因素
接收者受到通信数据后,类似上面所说的Nagle算法,接收者也会首先将数据保存至缓存中,由应用层程序主动从缓存中获取数据进行读取。这样就会造成如果接收数据的速度大于读取数据的速度,数据会被暂存至缓存,也就是多个数据包的分界不能区分,造成黏包问题。
处理办法
发送者处理
由于发送者造成的黏包问题主要是因为Nagle算法所导致,所以我们解决的方式也就是将Nagle算法禁用,进而解决由于发送者所造成的的黏包问题。使用TCP_NODELAY选项来关闭Nagle算法。
应用程序处理
由于接收端没有可以处理黏包问题的方法,我们只能从应用程序进行逻辑上的处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
|
import socket, struct, json import subprocess
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sk.bind(('127.0.0.1', 8080)) sk.listen(5)
while True: conn, addr = sk.accept() while True: cmd = conn.recv(1024) if not cmd: break print('cmd: %s' % cmd) res = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err = res.stderr.read() print(err) if err: back_msg = err else: back_msg = res.stdout.read()
headers={'data_size':len(back_msg)} head_json=json.dumps(headers) head_json_bytes=bytes(head_json,encoding='utf-8')
conn.send(struct.pack('i', len(head_json_bytes))) conn.send(head_json_bytes) conn.sendall(back_msg) conn.close()
from socket import * import struct, json
ip_port=('127.0.0.1',8080) client=socket(AF_INET,SOCK_STREAM) client.connect(ip_port)
while True: cmd=input('>>: ') if not cmd:continue client.send(bytes(cmd,encoding='utf-8'))
head=client.recv(4) head_json_len=struct.unpack('i',head)[0] head_json=json.loads(client.recv(head_json_len).decode('utf-8')) data_len=head_json['data_size']
recv_size=0 recv_data=b'' while recv_size < data_len: recv_data+=client.recv(1024) recv_size+=len(recv_data)
print(recv_data.decode('utf-8'))
|