黏包

黏包现象

同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接收到之前执行的另外一部分结果,这种现象就是黏包。只有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()
server端
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()
client端

高级

  

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()
server端
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()
client端
旗舰版
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()
server端
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()
client端
原文地址:https://www.cnblogs.com/biulo/p/10684621.html