什么是粘包:
一.当服务端发送的数据小于客户端接收数据缓存,且发送数据较快,客户端接收的数据就会出现粘包现象,这是由于tcp协议的优化导致,客户端接收数据时并不知道数据的边界。
二.当服务端发送的数据大于客户端接收数据的缓存,客户端需要多次从自己的缓存区提取数据,就出现了粘包现象
解决方法:
提前告诉客户端接收文件的大小
方法一: 每次发送数据时,获取数据长度,把数据长度发送给客户端,再发送真实数据
服务端代码:
from socket import * import subprocess import struct ip_port = ('127.0.0.1', 8000) back_log = 5 buffer_siza = 1024 tcp_server = socket(AF_INET, SOCK_STREAM) # tcp_server.setsockopt(SOL_SOCKET, SO_REUSEADDR) #如果当服务端关闭后再打开出现地址重用,无法启用,输入这代码即可解决 tcp_server.bind(ip_port) tcp_server.listen(back_log) while True: conn, addr = tcp_server.accept() print('新的客户端链接是:', addr) while True: try: cmd = conn.recv(buffer_siza).decode('utf-8') if not cmd: break print('收到,开始执行', cmd) p1 = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) stdder = p1.stderr.read() stdout = p1.stdout.read() size = len(stdder) + len(stdout) date = struct.pack('i', size) conn.send(date) conn.send(stdder) conn.send(stdout) print('ok') except Exception as e: break conn.close()
客户端代码:
from socket import * import struct ip_port = ('127.0.0.1', 8000) buffer_siza = 1024 tcp_clien = socket(AF_INET, SOCK_STREAM) tcp_clien.connect(ip_port) while True: msg = input('-->:') if not msg: continue if msg =='stop': break tcp_clien.send(msg.encode('utf-8')) recv_size = 0 recv_msg = b'' t_size = tcp_clien.recv(4) size = struct.unpack('i', t_size)[0] while recv_size < size: recv_msg += tcp_clien.recv(buffer_siza) recv_size = len(recv_msg) print(recv_msg.decode('gbk')) tcp_clien.close()
方法二:终极版
通过一个列表,该列表存储文件的一些信息,长度,文件名,md5等信息,先把列表发送,在通过列表里面存的文件大小再接收数据
服务端
from socket import * import subprocess import struct import json ip_port = ('127.0.0.1', 8000) back_log = 5 buffer_siza = 1024 tcp_server = socket(AF_INET, SOCK_STREAM) tcp_server.bind(ip_port) tcp_server.listen(back_log) while True: conn, addr = tcp_server.accept() print('新的客户端链接是:', addr) while True: try: cmd = conn.recv(buffer_siza).decode('utf-8') if not cmd: break print('收到,开始执行', cmd) p1 = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,) stdder = p1.stderr.read() stdout = p1.stdout.read() #建立字典 header_dic = { 'filename': 'a.txt', 'md5': 'xxxx', 'total_size': len(stdout) + len(stdder) } #序列化 header_json = json.dumps(header_dic) header_bytes = header_json.encode('utf-8') #发送经过序列化的报头长度 conn.send(struct.pack('i', len(header_bytes))) #发送序列化的真实数据 conn.send(header_bytes) conn.send(stdder) conn.send(stdout) print('ok') except Exception as e: break conn.close()
客户端:
from socket import * import struct import json ip_port = ('127.0.0.1', 8000) buffer_siza = 1024 tcp_clien = socket(AF_INET, SOCK_STREAM) tcp_clien.connect(ip_port) while True: msg = input('-->:') if not msg: continue if msg =='stop': break tcp_clien.send(msg.encode('utf-8')) recv_size = 0 recv_msg = b'' #接收报头长度 obj = tcp_clien.recv(4) header_size = struct.unpack('i', obj)[0] #接收报头信息 header_bytes = tcp_clien.recv(header_size) #反序列化,提取字典当中存的信息 header_json = header_bytes.decode('utf-8') header_dic = json.loads(header_json) total_size = header_dic['total_size'] while recv_size < total_size: recv_msg += tcp_clien.recv(buffer_siza) recv_size = len(recv_msg) print(recv_msg.decode('gbk')) tcp_clien.close()