Python中黏包现象及解决

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"))
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值