黏包现象
同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接收到之前执行的另外一部分结果,这种现象就是黏包。只有TCP协议会有黏包现象,UDP协议永远不会黏包
形成原因
原因一
在发送端,由于两条信息发送的间隔时间很短,且两条消息本身也很短,在发送之前被合并成一条信息
根据三次握手机制,发送方和接收方需要连续互相发送信息
而为了节省内存,减少互相发送信息次数
所以可能会直接把两条消息打包成一条发送给了接收方
造成了黏包现象
原因二
在接收端,由于接收不及时导致两条先后到达的信息在接收端黏在了一起
发送方发送信息后,接收端可能正在运行其他代码
在接收端可能会把收到的第一条信息先放下
在处理完后,会把放下的一二条信息同时打印出来
本质
信息与信息之间没有边界
这个现象是没有办法打破的
因为TCP协议是一种流式传输
解决方法
利用struct模块
struct模块可以把 +- 2147483647之间的数字转为4个字节
自定义协议
低级
import struct import socket sk = socket.socket() sk.bind(('127.0.0.1',9001)) sk.listen() conn,addr = sk.accept() str_msg = 'hello,你好么' byets_msg = str_msg.encode('utf-8') num = len(byets_msg) len_bytes = struct.pack('i',num) conn.send(len_bytes) conn.send(byets_msg) conn.send(b'world') conn.close() sk.close()
import time import struct import socket sk = socket.socket() sk.connect(('127.0.0.1',9001)) time.sleep(0.1) num= sk.recv(4) num = struct.unpack('i',num)[0] msg2 = sk.recv(num) print(msg2.decode('utf-8')) print(sk.recv(1024)) sk.close()
高级
import json import struct import socket sk = socket.socket() sk.bind(('127.0.0.1',9001)) sk.listen() conn,addr = sk.accept() num = conn.recv(4) num = struct.unpack('i',num)[0] str_dic = conn.recv(num).decode('utf-8') dic = json.loads(str_dic) with open(dic['filename'],'wb') as f: content = conn.recv(dic['filesize']) f.write(content) conn.close() sk.close()
import os import json import struct import socket sk = socket.socket() sk.connect(('127.0.0.1',9001)) filepath = input('请输入文件路径 :') filename = os.path.basename(filepath) filesize = os.path.getsize(filepath) dic = {'filename':filename,'filesize':filesize} str_dic = json.dumps(dic) bytes_dic = str_dic.encode('utf-8') len_dic = len(bytes_dic) bytes_len = struct.pack('i',len_dic) sk.send(bytes_len) sk.send(bytes_dic) with open(filepath,'rb') as f: content = f.read() sk.send(content) sk.close()
旗舰版
import socket import subprocess import struct import json phone = socket.socket() phone.bind(('127.0.0.1', 8888)) phone.listen(5) 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() result = result.decode('gbk').encode('utf-8') # print(f'服务端发送的总字节数{len(result)}') # 1. 制作报头 head_dict = { 'MD5': 'fdsaf2345544324dfs', 'file_name': '婚前视频', 'file_size': len(result), } # 2. 将报头字典转化成json字符串 head_dict_json = json.dumps(head_dict) # 3. 将json字符串 转化成bytes head_dict_json_bytes = head_dict_json.encode('utf-8') # 4. 获取报头的长度 head_len = len(head_dict_json_bytes) # 5.将长度转化成固定的4个字节 head_len_bytes = struct.pack('i',head_len) # 6. 发送固定的4个字节 conn.send(head_len_bytes) # 7. 发送报头 conn.send(head_dict_json_bytes) # 8. 发送原数据 conn.send(result) except ConnectionResetError: break conn.close() phone.close()
import json import struct import socket phone = socket.socket() phone.connect(('127.0.0.1',9799)) while 1: send_msg = input('>>>') phone.send(send_msg.encode('utf-8')) bytes_msg = phone.recv(4) len_msg = struct.unpack('i',bytes_msg)[0] s = b'' while len(s) < len_msg: s += phone.recv(1024) # dic = json.loads(s)['result'] dic = json.loads(s.decode('utf-8'))['result'] print(dic) phone.close()