黏包现象.
- 系统缓冲区:用户或服务端接收信息与传输介质之间的媒介。
- 缓冲区的作用:防止网络出现短暂的异常或波动而对计算机传输数据带来的直接影响。
- Remark:缓冲区解决了上传下载的传输效率的问题,带来了黏包问题。
系统缓冲区.
缓冲区的作用?
没有缓冲区:如果你的网络出现短暂的异常或者波动,接收数据就会出现短暂的中断,影响你的下载或者上传的效率.
但是 凡是都是双刃剑,缓冲区解决了上传下载的传输效率的问题,带来了黏包问题.
什么情况下产生黏包
- recv会产生黏包(如果recv接受的数据量(1024)小于发送的数据量,第一次只能接收规定的数据量1024,第二次接收剩余的数据量)
- send 也可能发生粘包现象.(连续send少量的数据发到输出缓冲区,由于缓冲区的机制,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络)
解决黏包的方案
low版
server
import socketimport subprocessimport structphone = socket.socket()phone.bind(('127.0.0.1', 8888))phone.listen(5)# 4. 接收连接print('start')conn, addr = phone.accept()while 1: try: cmd = conn.recv(1024) obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) result = obj.stdout.read() + obj.stderr.read() # print(f'服务端发送的总字节数{len(result)}') # 1. 制作报头 total_size = len(result) # 2. 将不固定长度的int类型的报头,转化成固定长度bytes 4个字节 # 将一个数字转化成等长度的bytes类型。 total_size_bytes = struct.pack('i', total_size) # 3. 发送报头 conn.send(total_size_bytes) # 4. 发送原数据 conn.send(result) except ConnectionResetError: breakconn.close()phone.close()
client
import socketimport structphone = socket.socket()phone.connect(('127.0.0.1', 8888))# 发消息while 1: cmd = input('>>>').strip() phone.send(cmd.encode('utf-8')) # 1. 接收报头 head_bytes = phone.recv(4) # 2. 将报头反解回int类型 total_size = struct.unpack('i', head_bytes)[0] # 1517 # 3. 循环接收原数据 total_data = b'' while len(total_data) < total_size: total_data += phone.recv(1024) print(total_data.decode('gbk'))# 关机phone.close()
旗舰版
server
import socketimport subprocessimport structimport jsonphone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)phone.bind(('127.0.0.1', 8878))phone.listen(5)while 1: conn, client_addr = phone.accept() print(client_addr) while 1: try: cmd = conn.recv(1024) ret = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) correct_msg = ret.stdout.read() error_msg = ret.stderr.read() # 1 制作固定报头 total_size = len(correct_msg) + len(error_msg) header_dict = { 'md5': 'fdsaf2143254f', 'file_name': 'f1.txt', 'total_size': total_size, } header_dict_json = json.dumps(header_dict) # str bytes_headers = header_dict_json.encode('utf-8') header_size = len(bytes_headers) header = struct.pack('i', header_size) # 2 发送报头长度 conn.send(header) # 3 发送报头 conn.send(bytes_headers) # 4 发送真实数据: conn.send(correct_msg) conn.send(error_msg) except ConnectionResetError: breakconn.close()phone.close()
client
import socketimport structimport jsonphone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)phone.connect(('127.0.0.1', 8878))while 1: cmd = input('>>>').strip() if not cmd: continue phone.send(cmd.encode('utf-8')) # 1,接收固定报头 header_size = struct.unpack('i', phone.recv(4))[0] # 2,解析报头长度 header_bytes = phone.recv(header_size) header_dict = json.loads(header_bytes.decode('utf-8')) # 3,收取报头 total_size = header_dict['total_size'] # 3,根据报头信息,接收真实数据 recv_size = 0 res = b'' while recv_size < total_size: recv_data = phone.recv(1024) res += recv_data recv_size += len(recv_data) print(res.decode('gbk'))phone.close()