ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Python中黏包现象及解决

2021-12-04 14:02:40  阅读:181  来源: 互联网

标签:struct Python 黏包 header length stderr 解决 长度 客户端


Python中黏包现象

什么是黏包现象 ?

因为TCP是基于数据流的,在服务端与客户端进行数据传输的时候,会自动将一小段一小段的数据打包成一个大分段的数据,然后传输,这样我们接受数据的时候就难以处理我们想要的数据
比如,我们想要在服务端给客户端发送数据,1041 ABCDAE,
serve.send(“1041”.encode(“utf8”))
serve.send(“ABCDAE”.encode(“utf8”))
我们想要在客户端先接受1041这段数据,然后接受ABCDAE
client.recv(1024)
client.recv(1024)
但是这样真的能接受到我们想要的结果吗,并不能,服务端发送的数据会以数据流的方式,将数据段1041以及数据段ABCDAE打包成一个数据段1041ABCDAE,然后以数据流的形式发送过去,在客户端接受了1024字节的数据就包含了1041ABCDAE这样的数据,我们就很难将1041 和 ABCDAE 分开

如何解决黏包现象 ?

我们先了解一下Python中的一个模块struct
ret = struct.pack(“i”,整型数据)可以将一个整型数据打包成一个固定字节的长度,
struct.unpack(“i”,ret)可以将我们刚刚的打包的数据在还原成一个元组(整型数据,)
这样我们可以先将数据内容的长度打包,发送给客户端,固定长度为4字节,然后在客户端就可以先接受4字节的内容,然后进行解包,拿到元组中第一个元素,就是数据内容的长度,然后通过长度的限制,循环取出我们想要的数据内容,这样就不担心黏包现象了

程序实现

服务端

import subprocess
import socket
import struct
serve = socket.socket()
serve.bind(("127.0.0.1",8000))
serve.listen(5)
while True:
    conn,addr = serve.accept()
    print("serve is working......")
    while True:
        cmd = conn.recv(1024).decode("utf8")
        if cmd=="exit":
            break
        else:
            #使用管道处理接受过来的DOS命令
            header_content = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
            #测试报错用,可以不用写这一行
            print("hello world")
            #获取管道中执行正确DOS命令的输出结果
            header_stdout = header_content.stdout.read()
            #获取管道中执行错误DOS命令的输出结果
            header_stderr = header_content.stderr.read()
            #如果执行了错误的DOS命令,则会执行这段代码,将错误信息返还给客户端
            if header_stderr:
                print("命令报错,请检查命令是否正确")
                header_stderr_length = len(header_stderr)
                #使用struct模块的pack方法,将内容长度打包成一个固定长度为4字节的内容先返回给客户端
                conn.send(struct.pack("i",header_stderr_length))
                #将内容在接着发送过去,这里必然会黏包
                conn.send(header_stderr)
            #否则执行正确dos命令的结果返回给客户端
            else:
                print("命令处理完成......")
                header_stdout_length = len(header_stdout)
                # 使用struct模块的pack方法,将内容长度打包成一个固定长度为4字节的内容先返回给客户端
                conn.send(struct.pack("i",header_stdout_length))
                # 将内容在接着发送过去,这里必然会黏包
                conn.send(header_stdout)
    conn.close()

客户端

import struct
import socket

client = socket.socket()
client.connect(("127.0.0.1",8000))
while True:
    cmd = input()
    if cmd=="exit":
        break
    elif cmd=="":
        continue
    client.send(cmd.encode("utf8"))
    #先接受前4个字节的内容,因为前4个字节我们使用struct模块的打包的是内容的长度
    header_length = client.recv(4)
    element_length = 0
    #对前4个字节进行解包,返回一个元组,元组第一个元素就是内容的长度,所以在这里取到内容的长度
    content_length = struct.unpack("i",header_length)[0]
    data = b''
    #接下来使用内容长度为限制条件,就可以继续接受后面的内容,这样即便数据会产生黏包,我们通过数据长度就可以对内容进行分割,取到我们想要的内容
    while element_length<content_length:
        cmd_content = client.recv(1024)
        element_length+=len(cmd_content)
        data+=cmd_content
    #输出打印接受的内容
    print(data.decode("gbk"))

标签:struct,Python,黏包,header,length,stderr,解决,长度,客户端
来源: https://blog.csdn.net/weixin_56319791/article/details/121714709

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有