粘包现象与解决方案

粘包

粘包是指两次输出结果粘到一起,它的发生主要是因为socket缓冲区导致的,粘包只在tcp中产生,不在UDP产生

粘包的解决方法:

使用struct模块,先报头长度进行打包发给客户端,客户端收到之后先解包报头长度,再接收真实的数据
例子:
服务端:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import socket
import subprocess
import struct
import json

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',9909)) #0-65535:0-1024给操作系统使用
phone.listen(5)

print('starting...')
while True: # 链接循环
    conn,client_addr=phone.accept()
    print(client_addr)

    while True: #通信循环
        try:
            #1、收命令
            cmd=conn.recv(8096)
            if not cmd:break #适用于linux操作系统

            #2、执行命令,拿到结果
            obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)

            stdout=obj.stdout.read()
            stderr=obj.stderr.read()

            #3、把命令的结果返回给客户端
            #第一步:制作固定长度的报头
            header_dic={
                'filename':'a.txt',
                'md5':'xxdxxx',
                'total_size': len(stdout) + len(stderr)
            }

            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(stdout)
            conn.send(stderr)

        except ConnectionResetError: #适用于windows操作系统
            break
    conn.close()

phone.close()

客户端:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import socket
import struct
import json

# 产生对象
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 进行连接
phone.connect(('192.168.43.129',8082))
while True:
    # 收发消息
    msg = input('>>:').strip()
    if not msg:continue
    phone.send(msg.encode('utf-8'))
    #先收报头长度
    obj = phone.recv(4)
    header_size = struct.unpack('i',obj)[0]
    #接收报头
    header_bytes = phone.recv(header_size)
    #解析对真实数据的描述信息
    header_json = header_bytes.decode('utf-8')
    header_dic = json.loads(header_json)
    total_size = header_dic['total_size']
    #接收真实的数据
    rev_size = 0
    rev_data = b''
    while rev_size < total_size: # 如果接收的数据小于从报头中解析出来的数据会一直循环进行接收
        res = phone.recv(1024)
        rev_data += res
        rev_size += len(res)
    print(rev_data.decode('utf-8'))

phone.close()
posted @ 2018-06-14 14:26  游走在边缘的人  阅读(158)  评论(0编辑  收藏  举报