簡體   English   中英

通過套接字python發送二進制文件的問題

[英]Problem sending binary files via sockets, python

我正在嘗試編寫一個程序,將二進制文件從客戶端傳輸到服務器。 這是代碼:

客戶端(發送文件)

  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

服務器(recv文件)

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 

這樣我可以傳輸txt文件,但是當我必須傳輸jpeg或任何其他類型的文件時,它會在循環中凍結。 有人可以解釋一下為什么嗎? 我是py的新手,我正在努力學習

如果雙方都具有相同的區域設置編碼,它不應該凍結,但它很容易死於異常。

你正在閱讀和發送二進制(好),但莫名其妙地decodestr ,然后encodebytes (壞)。 問題是,任何二進制數據都不能保證在任何給定的語言環境中都是可解碼的; 如果你的語言環境編碼是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)

作為ShadowRanger帖子的附錄,如果你想在不使用socket.sendfile情況下維護文件分塊,你可以利用一些技巧來清理你的代碼並減少內存占用。

發送過程非常簡單,因為我們復制了從ShadowRanger發送文件大小的過程,並添加了一個非常簡單的循環來發送數據塊,直到塊空出來(文件末尾)。

def send_file(self,filename):
    print("Sending: " + filename)
    #send file size as big endian 64 bit value (8 bytes)
    self.sock.sendall(os.stat(filename).st_size.tobytes(8,'big'))
    with open(filename,'rb') as f: #open our file to read
        while True:
            chunk = f.read(self.BUFFER_SIZE) #get next chunk
            if not chunk: #empty chunk indicates EOF
                break
            self.sock.sendall(chunk) #send the chunk

接收文件也非常簡單,使用相同的過程在開始時讀取預期的文件大小,然后在我們達到預期大小之前將數據讀入該文件。 然后我們使用f.tell()作為接收數據,這是一種告訴整個文件是否已經發送的簡單方法。

def recv_file(self, conn, filename):
    # file size transfer copied from ShadowRanger
    # Get the expected length (eight bytes long, always)
    expected_size = b"" #buffer to read in file size
    while len(expected_size) < 8: #while buffer is smaller than 8 bytes
        more_size = conn.recv(8 - len(expected_size)) #read up to remaining bytes
        if not more_size: #nothing was read
            raise Exception("Short file length received")
        expected_size += more_size #extend buffer
    expected_size = int.from_bytes(expected_size, 'big') #Convert to int, the expected file length
    with open(filename, 'wb') as f: #open our file to write
        while f.tell() < expected_size: #while it's smaller than our expected size
            bytes_recvd = conn.recv() #read any available data 
            f.write(bytes_recvd)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM