解决粘包现象

Posted Mr。yang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解决粘包现象相关的知识,希望对你有一定的参考价值。

简单版

服务端

 

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
# Author:Mr.yang
import socket
import struct
import subprocess

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind((127.0.0.1,8080))
phone.listen(5)

print(setting...)
while True: # 链接循环
    conn,A = phone.accept()
    while True: # 通信循环
        try:
            #1.接收数据
            data = conn.recv(8096)
            print(客户端数据:,data)

            #2.执行命令,拿到结果
            obj = subprocess.Popen(data.decode(utf-8), shell=True,  # shell 可以解析前面的字符串,相当于cmd
                                   stdout=subprocess.PIPE,  # 正确管道
                                   stderr=subprocess.PIPE)  # 错误管道

            stdout = obj.stdout.read() #bytes类型
            stderr = obj.stderr.read()

            #3.将结果返回给客户端
            #第一步: 制定固定长度的报头
            total_size = len(stdout) + len(stderr)
            header = struct.pack(i,total_size)

            #第二步: 把报头发送给客户端
            conn.send(header)

            #第三步: 在发送真实的数据
            conn.send(stdout)
            conn.send(stderr) #分开写的原因,TCP会自动粘包,可以起到优化的作用

        except ConnectionResetError:
            break
    conn.close
phone.close()

 

客户端

 

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
# Author:Mr.yang
‘‘‘
recv不能设置数值特别大的原因:
1.在接收文件的时候可能超过指定数值
2.自己的操作系统的缓存不能无限大,最大也不能超过操作系统的缓存
‘‘‘
import socket,struct
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect((127.0.0.1,8080))

while True:
    #1.发命令
    cmd = input(>>:).strip()
    if not cmd:continue
    phone.send(cmd.encode(utf-8))

    #2.拿到命令结果

    #第一步: 先收报头
    header = phone.recv(4)

    #第二步: 从报头中解析出对真实数据的描述信息 (数据的长度)
    tolal_size = struct.unpack(i,header)[0]

    #第三步: 接收真实的数据
    recv_size=0
    recv_data=b‘‘
    while recv_size < tolal_size:
        res = phone.recv(100)
        recv_data += res
        recv_size += len(res)

    print(recv_data.decode(gbk))
phone.close()

 

终极版

服务端

 

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
# Author:Mr.yang
import socket
import struct
import json
import subprocess
‘‘‘
相对于简单版的优化
1.报头信息少
2.struct的i格式有限制
‘‘‘

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind((127.0.0.1,8080))
phone.listen(5)

print(setting...)
while True: # 链接循环
    conn,A = phone.accept()
    while True: # 通信循环
        try:
            #1.接收数据
            data = conn.recv(8096)
            print(客户端数据:,data)

            #2.执行命令,拿到结果
            obj = subprocess.Popen(data.decode(utf-8), shell=True,  # shell 可以解析前面的字符串,相当于cmd
                                   stdout=subprocess.PIPE,  # 正确管道
                                   stderr=subprocess.PIPE)  # 错误管道

            stdout = obj.stdout.read() #bytes类型
            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) #分开写的原因,TCP会自动粘包

        except ConnectionResetError:
            break
    conn.close
phone.close()

 

客户端

 

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
# Author:Mr.yang
‘‘‘
recv不能设置数值特别大的原因:
1.在接收文件的时候可能超过指定数值
2.自己的操作系统的缓存不能无限大,最大也不能超过操作系统的缓存
‘‘‘
import socket
import struct
import json

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect((127.0.0.1,8080))

while True:
    #1.发命令
    cmd = input(>>:).strip()
    if not cmd:continue
    phone.send(cmd.encode(utf-8))

    #2.拿到命令结果

    #第一步: 先收报头的长度
    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)
    print(header_dic)
    total_size = header_dic[total_size]

    #第四步: 接收真实的数据
    recv_size=0
    recv_data=b‘‘
    while recv_size < total_size:
        res = phone.recv(1024)
        recv_data += res
        recv_size += len(res)

    print(recv_data.decode(gbk))
phone.close()

 

以上是关于解决粘包现象的主要内容,如果未能解决你的问题,请参考以下文章

透过现象看本质,我找到了Netty粘包与半包的这几种解决方案。

python3全栈开发-什么是粘包粘包现象如何解决粘包

解决粘包现象

python 解决粘包现象(struct模块)

Netty进阶——粘包与半包(代码示例)

5 粘包现象与解决方案