Следующий код устанавливает соединение клиент-сервер между двумя программами Python с помощью стандартного модуля socket и отправляет файл с клиента на сервер.
Логика передачи файлов содержится в двух функциях: клиент определяет функцию send_file() для отправки файла через сокет и, наоборот, сервер определяет функцию receive_file() для его получения.
Таким образом, вы сможете легко перенести функции из этой статьи в другие программы, которые уже реализуют соединение клиент-сервер.
Код подготовлен для отправки файлов любого формата и любого размера. Требуется Python 3.8 или выше.
Сервер:
# server.py
import socket
import struct
def receive_file_size(sck: socket.socket):
# Эта функция обеспечивает получение байтов,
# указывающих на размер отправляемого файла,
# который кодируется клиентом с помощью
# struct.pack(), функции, которая генерирует
# последовательность байтов, представляющих размер файла.
fmt = "<Q"
expected_bytes = struct.calcsize(fmt)
received_bytes = 0
stream = bytes()
while received_bytes < expected_bytes:
chunk = sck.recv(expected_bytes - received_bytes)
stream += chunk
received_bytes += len(chunk)
filesize = struct.unpack(fmt, stream)[0]
return filesize
def receive_file(sck: socket.socket, filename):
# Сначала считываем из сокета количество
# байтов, которые будут получены из файла.
filesize = receive_file_size(sck)
# Открываем новый файл для сохранения
# полученных данных.
with open(filename, "wb") as f:
received_bytes = 0
# Получаем данные из файла блоками по
# 1024 байта до объема
# общего количество байт, сообщенных клиентом.
while received_bytes < filesize:
chunk = sck.recv(1024)
if chunk:
f.write(chunk)
received_bytes += len(chunk)
with socket.create_server(("localhost", 6190)) as server:
print("Ожидание клиента...")
conn, address = server.accept()
print(f"{address[0]}:{address[1]} подключен.")
print("Получаем файл...")
receive_file(conn, "image-received.png")
print("Файл получен.")
print("Соединение закрыто.")
Code language: PHP (php)
Клиент
# client.py
import os
import socket
import struct
def send_file(sck: socket.socket, filename):
# Получение размера файла.
filesize = os.path.getsize(filename)
# В первую очередь сообщим серверу,
# сколько байт будет отправлено.
sck.sendall(struct.pack("<Q", filesize))
# Отправка файла блоками по 1024 байта.
with open(filename, "rb") as f:
while read_bytes := f.read(1024):
sck.sendall(read_bytes)
with socket.create_connection(("localhost", 6190)) as conn:
print("Подключение к серверу.")
print("Передача файла...")
send_file(conn, "image.png")
print("Отправлено.")
print("Соединение закрыто.")
Code language: PHP (php)
Чтобы протестировать код, вы должны изменить вызовы send_file() и receive_file(), указав путь к файлу, который вы хотите отправить, и путь к файлу, в котором вы хотите его получить.
В текущем коде это файл image.png, который находится в той же папке, что и два файла Python, и принимается как image-received.png.
После этого запустите сервер:
python server.py
Code language: CSS (css)
Затем, на другом терминале, клиент:
python client.py
Code language: CSS (css)
Вывод сервера будет выглядеть следующим образом:
Ожидание клиента...
127.0.0.1:60331 подключен.
Получаем файл...
Файл получен.
Соединение закрыто.
Code language: CSS (css)
Что касается клиента:
Подключение к серверу. Передача файла... Отправлено. Соединение закрыто.