目錄

廣告 AD

透過Socket傳送檔案:簡單檔案傳輸的方法

前陣子想要透過 socket 傳送檔案

於是找了下資料

做個筆記

廣告 AD

在使用 socket 接收字串的時候,由於不知道這個訊息的字串有多長

我們可能會用換行 (\n) 來當作分隔兩個訊息的分隔符號 (delimiter)

但是這種做法要確保你傳送的訊息內不能有分隔符號

如果訊息內有包含分隔符號就需要另外做判斷

因此這種做法不太好,我們可以試試看以下方法:

在傳送資料之前,我們可以先傳送資料長度

接著再傳送資料本體

如此一來,接收方就知道這次訊息有多少長度

就不會混淆兩個連續的訊息了


要想透過 socket 傳送檔案,我們需要傳送以下資料:

  1. 檔案名稱
  2. 檔案大小
  3. 檔案內容

由於 socket 傳送時要使用 byte 傳送

檔案名稱我們可以透過 encode 轉成 bytes

接收後再透過 decode 轉回 string

Python

fileName = 'test.txt'
fileName_bytes = fileName.encode('utf-8')
fileName_str = fileName_bytes.decode('utf-8')

我們可以透過 os.path.getsize 取得檔案大小

接著再透過 to_bytes 轉成 bytes

這時候要注意我們要轉成的 bytes 的大小

一般使用 4 個 bytes 即可,也就是用 32 個 bit 表示數字

如果你的檔案大小更大的話,可以往上調整表示的 bytes 數量

接著要設定 bytes 的排列順序,這裡使用的是 little endian

你也可以設定成 big endian,只要接收和傳送是一致的就好

最後透過 int.from_bytes 將 bytes 轉回 int

Python

import os
fileSize = os.path.getsize('test.txt')
fileSize_bytes = fileSize.to_bytes(4, 'little')
fileSize_int = int.from_bytes(fileSize_bytes, 'little')

最後傳送檔案本體就好,記得要用 binary 的方式開啟

接收時,輸出也要是 binary 方式

Python

with open(FILE, "rb") as f:
    bytes = f.read(1024)
    while bytes:
        bytes = f.read(1024)

Python

with open(file_name, "wb") as f:
    while True:
        bytes = client.recv(1024)
        file_size -= len(bytes)
        f.write(bytes)
        if file_size == 0: break

以下簡單寫了個 Server 和 Client 的範例

Server:

Python

import socket
import os

HOST = '127.0.0.1'
PORT = 8000
FILE = "file.data"

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((HOST, PORT))
server.listen(1)

conn, addr = server.accept()
    
with open(FILE, "rb") as f:
    # file name
    file_name_bytes = FILE.encode("utf-8")
    file_name_bytes_len = len(file_name_bytes)
    conn.sendall(file_name_bytes_len.to_bytes(4, 'little'))
    conn.sendall(file_name_bytes)
    print(f"File Name: {FILE}")

    # file size
    file_size = os.path.getsize(FILE)
    conn.sendall(file_size.to_bytes(4, 'little'))
    print(f"File Size: {file_size} (bytes)")

    # file content
    bytes = f.read(1024)
    while bytes:
        conn.sendall(bytes)
        bytes = f.read(1024)
    
conn.close()

Client:

Python

import socket

HOST = "127.0.0.1"
PORT = 8000

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((HOST, PORT))

# file name
file_name_bytes_len = int.from_bytes(client.recv(4), "little")
file_name = client.recv(file_name_bytes_len).decode("utf-8")
print(f"File Name: {file_name}")

# file size
file_size = int.from_bytes(client.recv(4), "little")
print(f"File Size: {file_size} (bytes)")

# file content
with open(file_name, "wb") as f:
    while True:
        bytes = client.recv(1024)
        file_size -= len(bytes)
        f.write(bytes)
        if file_size == 0: break

client.close()

廣告 AD