欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

python之黏包和黏包解决方案

程序员文章站 2022-03-16 23:04:31
黏包现象主要发生在TCP连接, 基于TCP的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看来,根本不知道该文件的字节流从何处开始,在何处结束. 两种黏包现象: 1 连续的小包可能会被优化算法给组合到一起进行发送 2 第一次如果发送的数据大小2000B接收端一次性 ......

黏包现象主要发生在tcp连接, 基于tcp的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看来,根本不知道该文件的字节流从何处开始,在何处结束.

两种黏包现象:

1 连续的小包可能会被优化算法给组合到一起进行发送

2 第一次如果发送的数据大小2000b接收端一次性接受大小为1024,这就导致剩下的内容会被下一次recv接收到,导致结果错乱

解决黏包现象的两种方案:

方案一:由于双方不知道对方发送数据的长度,导致接收的时候,可能接收不全,或者多接收另外一次发送的信息内容,所以在发送真实数据之前,要先发送数据的长度,接收端根据长度来接收后面的真实数据,但是双方有一个交互确认的过程

# 服务端
import socket
import subprocess
server = socket.socket()
ip_port = ('127.0.0.1',8001)

server.bind(ip_port)

server.listen()

conn,addr = server.accept()

while 1:
    from_client_cmd = conn.recv(1024)

    print(from_client_cmd.decode('utf-8'))
    #接收到客户端发送来的系统指令,我服务端通过subprocess模块到服务端自己的系统里面执行这条指令
    sub_obj = subprocess.popen(
        from_client_cmd.decode('utf-8'),
        shell=true,
        stdout=subprocess.pipe,  #正确结果的存放位置
        stderr=subprocess.pipe   #错误结果的存放位置
    )
    #从管道里面拿出结果,通过subprocess.popen的实例化对象.stdout.read()方法来获取管道中的结果
    std_msg = sub_obj.stdout.read()

    #为了解决黏包现象,我们统计了一下消息的长度,先将消息的长度发送给客户端,客户端通过这个长度来接收后面我们要发送的真实数据
    std_msg_len = len(std_msg)
    # std_bytes_len = bytes(str(len(std_msg)),encoding='utf-8')
    #首先将数据长度的数据类型转换为bytes类型
    std_bytes_len = str(len(std_msg)).encode('utf-8')
    print('指令的执行结果长度>>>>',len(std_msg))
    conn.send(std_bytes_len)

    status = conn.recv(1024)
    if status.decode('utf-8') == 'ok':

        conn.send(std_msg)
    else:
        pass
# 客户端
import socket

client = socket.socket()
client.connect(('127.0.0.1',8001))

while 1:
    cmd = input('请输入指令:')
    client.send(cmd.encode('utf-8'))

    server_res_len = client.recv(1024).decode('utf-8')
    print('来自服务端的消息长度',server_res_len)

    client.send(b'ok')

    server_cmd_result = client.recv(int(server_res_len))
    print(server_cmd_result.decode('gbk'))

方案二:

  struct模块,

  打包:struct.pack(‘i’,长度)

  解包:struct.unpack(‘i’,字节)

# 服务端
import socket
import subprocess
import struct
server = socket.socket()
ip_port = ('127.0.0.1',8001)

server.bind(ip_port)

server.listen()

conn,addr = server.accept()

while 1:
    from_client_cmd = conn.recv(1024)

    print(from_client_cmd.decode('utf-8'))
    #接收到客户端发送来的系统指令,我服务端通过subprocess模块到服务端自己的系统里面执行这条指令
    sub_obj = subprocess.popen(
        from_client_cmd.decode('utf-8'),
        shell=true,
        stdout=subprocess.pipe,  #正确结果的存放位置
        stderr=subprocess.pipe   #错误结果的存放位置
    )
    #从管道里面拿出结果,通过subprocess.popen的实例化对象.stdout.read()方法来获取管道中的结果
    std_msg = sub_obj.stdout.read()

    #为了解决黏包现象,我们统计了一下消息的长度,先将消息的长度发送给客户端,客户端通过这个长度来接收后面我们要发送的真实数据
    std_msg_len = len(std_msg)

    print('指令的执行结果长度>>>>',len(std_msg))
    msg_lenint_struct = struct.pack('i',std_msg_len)
    conn.send(msg_lenint_struct+std_msg)
# 客户端
import socket
import struct
client = socket.socket()
client.connect(('127.0.0.1',8001))

while 1:
    cmd = input('请输入指令:')
    #发送指令
    client.send(cmd.encode('utf-8'))

    #接收数据长度,首先接收4个字节长度的数据,因为这个4个字节是长度
    server_res_len = client.recv(4)
    msg_len = struct.unpack('i',server_res_len)[0]

    print('来自服务端的消息长度',msg_len)
    #通过解包出来的长度,来接收后面的真实数据
    server_cmd_result = client.recv(msg_len)
    print(server_cmd_result.decode('gbk'))