小白学python-------------网络编程之粘包问题及终极解决方案(tcp流式协议)

一.什么是粘包问题

tcp协议通过negle算法会将数据量较小,发送时间间隔较短的多个数据包合并一个发送

二.如何解决粘包问题

解决粘包问题就在于要知道每个包的数据大小,然后在准确的接受就可以了.


这里使用自定义报头的方法,思路:

         1.先发固定的报头长度,比如4个bytes(基于struct可以把任意类型转换会固定的bytes)

         2.先发送自定义报头(报头中要包含真正数据的一些信息,如文件名,文件大小,md5值等等)

         3.再发送真正的数据


关键就在于如何制作包含真正数据信息的报头

1.报头要包含真正数据的信息,那么我们可以用字典的形式记录下来(header_dic).

2.利于json模块把字典序列化成json格式的字符串(header_json)

3.既然是字符串肯定可以encode,那么把json格式的字符串encode成bytes类型(header_bytes).

4.然后用len方法计算长度(header_len=len(header_bytes))

5.利用struct把header_len转换为固定的4个bytes

6.发送固定的4个bytes(接收端首先也接受固定的4个bytes)

7.再发送报头数据header_bytes

8.再发送真正的数据

9.接受端先接受固定的4个bytes,然后再接受报头然后反解出来,最后再根据反解得到的header_dic里的内容(如文件名,文件大小,md5值等等)接受真正的数据.


服务端

import subprocess
from socket import *
import struct
import json

server = socket(AF_INET,SOCK_STREAM)
HOST = '127.0.0.1'
PORT = 8080
ADDR = (HOST,PORT)
BUFSIZE = 1024

server.bind((ADDR))

server.listen(5)

while True:
    conn,addr = server.accept()
    print('客户端:',addr)
    while True:
        try:
            cmd = conn.recv(BUFSIZE)
            if len(cmd) == 0:break
            print(cmd.decode('utf-8'))

            obj = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)

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

            # 1.先制作报头
            header_dic = {'filename':'a.txt',
                          'total_size':len(stdout)+len(stderr),
                          'hash':'qwerqw165q45e1qew5631'}

            #2.将报头转化为json格式的字符串
            header_json = json.dumps(header_dic)

            #3.将json格式的字符串转化为bytes
            header_bytes = header_json.encode('utf-8')
            print(header_bytes)
            #4.再将bytes类型的json文件通过模块struct转化为固定长度的bytes
            # struct.pack('i',header_bytes)
            #5.发送报头的的固定长度
            conn.send(struct.pack('i',len(header_bytes)))

            #6.发送报头
            conn.send(header_bytes)

            # 7.再发送真实的数据
            conn.send(stdout)
            conn.send(stderr)
        except ConnectionResetError:
            break

    conn.close()
客户端

import struct
import json
from socket import *

client = socket(AF_INET,SOCK_STREAM)
HOST = '127.0.0.1'
PORT = 8080
ADDR = (HOST,PORT)
BUFSIZE = 1024# 再大不能超过8096

client.connect(ADDR)
while True:
    msg = input('>>>:').strip()
    if not msg:continue
    client.send(msg.encode('utf-8'))

    res = b''
    recv_size = 0
    #1.先拿到报头的固定长度4,然后把4个bytes通过struct模块的unpack并反转换成报头的大小数字
    header_len = struct.unpack('i',client.recv(4))[0]
    #2.通过大小信息拿到报头的bytes信息
    header_bytes = client.recv(header_len)
    #3.将bytes反解成json格式的字符串
    header_json = header_bytes.decode('utf-8')
    #4.将json格式反解成报头字典信息
    header_dic = json.loads(header_json)
    #5.取出真实数据的文件大小信息
    total_size = header_dic['total_size']

    while recv_size<total_size:
        #6.接受真正的数据
        data = client.recv(BUFSIZE)
        res+=data
        recv_size+=len(data)
    print(res.decode('gbk'))

猜你喜欢

转载自blog.csdn.net/qq_42721964/article/details/82348209