Ускорить передачу файлов с помощью сокета

Передача файлов с использованием потока сокетов выполняется слишком медленно. Почти 100кбит/с. Я использовал модуль сокета Python, чтобы сделать этот код. Он отправляет данные клиенту, когда клиент отправляет имя файла. Как я могу увеличить скорость этой штуки?

Ниже код сервера

import socket
import os

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 9999))
server.listen()

client, addr = server.accept()

msg = client.recv(1024).decode()

file = open("Server_files/"+msg, "rb")
file_size = os.path.getsize("Server_files/"+msg)

# client.send("Received_image.png".encode())
client.send(str(file_size).encode())

data = file.read()
client.sendall(data)
client.send(b"<END>")

file.close()
client.close()

Ниже приведен код клиента

import tqdm
import socket

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('localhost', 9999))

file_name = input("Enter the file name: ")

client.send(file_name.encode())

file_size = client.recv(1024).decode()
print(file_size)

file = open(file_name, "wb")

file_bytes = b""

done = False

progress = tqdm.tqdm(unit = "B", unit_scale=True, unit_divisor=1000,
                     total=int(file_size))

while not done:
    data = client.recv(1024)
    if file_bytes[-5:] == b"<END>":
        done = True
    else:
        file_bytes += data
    progress.update(1024)

file.write(file_bytes)

file.close()
client.close()

Обратите внимание, что TCP не является протоколом сообщений. 1 отправка не всегда равна 1 получению того же размера, поэтому отправьте имя файла и размер файла с разделителем, например новой строкой, и буферизируйте получение, пока у вас не будет полной строки.

Mark Tolonen 16.12.2022 15:04
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
71
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вместо того, чтобы постоянно добавлять входящие данные в file_bytes (что потенциально требует много оперативной памяти, а также требует, чтобы Python несколько раз перераспределял буфер большего размера), вы должны просто записывать входящие data непосредственно в file по мере их получения.

Также может помочь увеличить размер чанка с 1024 до чего-то большего, может быть 128*1024; это позволило бы системным вызовам выполнять больше работы при каждом вызове.

Ответ принят как подходящий

Вот полное решение (Python 3.10), которое позволяет быстро загружать несколько файлов. Он использует socket.makefile для буферизации данных, with для автоматического закрытия сокетов, файлов и сброса tqdm прогресса, а также записывает данные как полученные для скорости.

Обратите внимание, что TCP не является протоколом обмена сообщениями. Он отправляет байты и получает байты в том же порядке, но не обязательно с одинаковыми «разрывами». Таким образом, send('filename') и send('filesize') могут быть recv(1024) как 'filenamefilesize' или 'filenamefi' и 'lesize' или любым другим разрывом. Итак, определите протокол:

Сервер:

  1. прочитать имя файла, за которым следует '\n'.
  2. Если получено 0 байт, соединение было закрыто.
  3. отправьте размер файла в виде строки в базе 10, за которой следует '\n'.
  4. отправить точно "filesize" байт данных файла.
  5. повторить.

Клиент:

  1. отправить имя файла, за которым следует '\n'
  2. прочитать размер файла, за которым следует '\n', преобразовать в целое число по основанию 10.
  3. читать точно "размер файла" байтов данных файла
  4. если сделано, закройте соединение, иначе повторите.

сервер.py

import socket
import os

BLOCK = 128 << 10 # 128KB

with socket.socket() as server:
    server.bind(('', 9999))
    server.listen()

    while True:
        client, addr = server.accept()
        try:
            with (client,
                  client.makefile('rb') as rfile,
                  client.makefile('wb') as wfile):

                while True:
                    filename = rfile.readline()
                    if not filename: break

                    fullname = os.path.join('Server_files', filename.decode().rstrip('\n'))
                    file_size = os.path.getsize(fullname)
                    wfile.write(f'{file_size}\n'.encode())
                    print(f'Sending {fullname}...')
                    with open(fullname, 'rb') as file:
                        while data := file.read(BLOCK):
                            wfile.write(data)
                    wfile.flush() # make sure anything remaining in makefile buffer is sent.
                    print(f' Complete ({file_size} bytes).')
        except ConnectionResetError:
            print('Client aborted.')

клиент.py

import socket
import tqdm

BLOCK = 1 << 20  # 1MB

with socket.socket() as client:
    client.connect(('localhost', 9999))

    with (client.makefile('rb') as rfile,
          client.makefile('wb') as wfile):

        while True:
            file_name = input('File name (just ENTER to quit): ')
            if not file_name: break
            wfile.write(f'{file_name}\n'.encode())
            wfile.flush() # make sure makefile buffer is fully sent
            file_size = int(rfile.readline().decode())

            with (tqdm.tqdm(unit='B',
                            unit_scale=True,
                            unit_divisor=1000,
                            total=file_size) as progress,
                  open(file_name, 'wb') as file):

                remaining = file_size
                while remaining:
                    data = rfile.read(BLOCK if remaining > BLOCK else remaining)
                    file.write(data)
                    progress.update(len(data))
                    remaining -= len(data)

Пример вывода (обратите внимание на скорость загрузки):

File name (just ENTER to quit): bigfile1
100%|██████████████████████████████████████████████████████████| 191M/191M [00:00<00:00, 706MB/s]
File name (just ENTER to quit): bigfile2
100%|██████████████████████████████████████████████████████████| 218M/218M [00:00<00:00, 718MB/s]
File name (just ENTER to quit):

Другие вопросы по теме