通过套接字发送二进制文件时出现问题,python

Mar*_*ino 3 python sockets file

我正在尝试编写一个将二进制文件从客户端传输到服务器的程序。这是代码:

客户端(发送文件)

  def send_file(self,filename):
        print("Sending: " + filename)
        size = self.BUFFER_SIZE
        with open(filename,'rb') as f:
            raw = f.read().decode()
        buffer = [raw[i:i + size] for i in range(0, len(raw), size)]
        for x in range(len(buffer)):
            self.sock.sendall(buffer[x].encode())

        return
Run Code Online (Sandbox Code Playgroud)

服务器(接收文件)

def recv_file(self, conn, filename):
    packet = ""
    buffer = ""
    while True:
        buffer = conn.recv(self.BUFFER_SIZE)
        packet = packet + str(buffer.decode())
        if not len(buffer) == self.BUFFER_SIZE:
            break
    with open(filename, 'wb') as f:
        f.write(bytes(packet.encode()))
    #print(packet)
    return 
Run Code Online (Sandbox Code Playgroud)

这样我可以传输 txt 文件,但是当我必须传输 jpeg 或任何其他类型的文件时,它会在循环中冻结。有人可以解释一下为什么吗?我是 py 新手,我正在努力学习

Sha*_*ger 5

如果双方具有相同的区域设置编码,它不应该冻结,但它很容易因异常而死亡。

您正在以二进制形式读取和发送(好),但莫名其妙地decode-ing 到str,然后又encodeing 返回bytes(坏)。问题是,不能保证任意二进制数据在任何给定的语言环境中都是可解码的;如果您的区域设置编码是 UTF-8,那么它很可能是不合法的。如果是的话latin-1,这是合法的,但毫无意义。

更糟糕的是,如果您的客户端和服务器具有不同的区域设置编码,则双方的解码结果可能不同(因此长度不匹配)。

一致使用bytes,不要在字符串之间进行转换,区域设置也无关紧要。您的代码也会运行得更快。您还需要提前实际发送文件长度;您的循环希望recv仅在文件完成时返回较短的长度,但如果:

  1. 该文件是缓冲区大小的精确倍数,或者
  2. 套接字发送的数据块与缓冲区大小不匹配

你们每个人都可以得到简短的recv结果,在情况 #2 中是巧合,在情况 #1 中是确定性的。

更安全的方法是在传输中实际添加文件长度前缀,而不是希望分块按预期工作:

def send_file(self,filename):
    print("Sending:", filename)
    with open(filename, 'rb') as f:
        raw = f.read()
    # Send actual length ahead of data, with fixed byteorder and size
    self.sock.sendall(len(raw).to_bytes(8, 'big'))
    # You have the whole thing in memory anyway; don't bother chunking
    self.sock.sendall(raw)

def recv_file(self, conn, filename):
    # Get the expected length (eight bytes long, always)
    expected_size = b""
    while len(expected_size) < 8:
        more_size = conn.recv(8 - len(expected_size))
        if not more_size:
            raise Exception("Short file length received")
        expected_size += more_size

    # Convert to int, the expected file length
    expected_size = int.from_bytes(expected_size, 'big')

    # Until we've received the expected amount of data, keep receiving
    packet = b""  # Use bytes, not str, to accumulate
    while len(packet) < expected_size:
        buffer = conn.recv(expected_size - len(packet))
        if not buffer:
            raise Exception("Incomplete file received")
        packet += buffer
    with open(filename, 'wb') as f:
        f.write(packet)
Run Code Online (Sandbox Code Playgroud)