Показывает ход загрузки и одновременно выполняемые потоки в сокете передачи файлов python.Python

Программы на Python
Ответить Пред. темаСлед. тема
Anonymous
 Показывает ход загрузки и одновременно выполняемые потоки в сокете передачи файлов python.

Сообщение Anonymous »

У меня есть две проблемы, требующие помощи ниже. Передача файловых сокетов Python и многопоточность

Код: Выделить всё

# server.py
import os
import socket
import threading

RESOURCES_SERVER = 'resources'
TEXT_FILE = 'text.txt'
BUFFER_SIZE = 4096

init_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
init_server.bind((socket.gethostname(), 9876))
init_server.listen(5)
file_lock = threading.Lock()
print(f"Server started at {init_server.getsockname()}")

def handle_client(server, addr):
try:
text_file = os.path.join(os.path.dirname(__file__), TEXT_FILE)
resources_dir = os.path.join(os.path.dirname(__file__), RESOURCES_SERVER)

with open(text_file, 'r') as f:
file_list = f.read()
server.send(file_list.encode())

while True:
request = server.recv(BUFFER_SIZE).decode().strip()
if not request:
break

if '|' not in request:
file_path = os.path.join(resources_dir, request)
if os.path.isfile(file_path):
file_size = os.path.getsize(file_path)
server.send(str(file_size).encode())
else:
server.send(f"File {request} not found.".encode())
continue

parts = request.split('|')
if len(parts) != 2:
server.send("Invalid request.".encode())
continue

file_name, range_str = parts
start, end = map(int, range_str.split('-'))
file_path = os.path.join(resources_dir, file_name)

if not os.path.isfile(file_path):
server.send(f"File {file_name} not found.".encode())
continue

with file_lock:
with open(file_path, 'rb') as f:
f.seek(start)
remaining = end - start

while remaining > 0:
chunk_size = min(BUFFER_SIZE, remaining)
chunk = f.read(chunk_size)
if not chunk:
break
server.sendall(chunk)
remaining -= len(chunk)

except ConnectionResetError:
print(f"Connection reset by peer {addr}")
except Exception as e:
print(f"Error handling client {addr}: {e}")
finally:
server.close()

def run_server():
write_text()
try:
while True:
server, addr = init_server.accept()
print(f"Connected by {addr}")
thread = threading.Thread(target=handle_client, args=(server, addr))
thread.daemon = True
thread.start()
except KeyboardInterrupt:
print("\nServer is shutting down...")
finally:
server.close()

if __name__ == "__main__":
run_server()

Код: Выделить всё

# client.py
import os
import socket
import threading
import sys
import time

REQUEST_DOWNLOAD_FILE = "input.txt"
DOWNLOAD_FOLDER = "data"
BUFFER_SIZE = 4096
PORT = 9876

lock = threading.Lock()
last_used_line_in_terminal = 0

...

def display_progress_download(base_line, part_num, percent):
with lock:
target_line = base_line + part_num
sys.stdout.write(f'\033[{target_line}H')
sys.stdout.write('\033[2K')
sys.stdout.write(f"Part {part_num} - Progress: {percent:.2f}% / 100%\r")
sys.stdout.flush()
time.sleep(0.01)

def download_part(HOST, PORT, file_name, part_num, start, end, base_line, part_size):
client = None
try:
client = create_connection_to_server(HOST, PORT)
if not client:
return False

client.recv(BUFFER_SIZE)

request = f"{file_name}|{start}-{end}".encode()
client.sendall(request)

part_path = f"./{DOWNLOAD_FOLDER}/{file_name}.part{part_num}"
with open(part_path, "wb") as f:
received = 0
while received <  part_size:
chunk_size = min(BUFFER_SIZE, part_size - received)
data = client.recv(chunk_size)
if not data:
raise ConnectionError(f"Connection lost at {received} bytes")
f.write(data)
received += len(data)
percent = min(received / part_size * 100, 100)
display_progress_download(base_line, part_num, percent)
return True

except Exception as e:
print(f"Error downloading part {part_num}: {e}")
return False
finally:
if client:
client.close()

def merge_parts(file_name, parts):
output_path = f"./{DOWNLOAD_FOLDER}/{file_name}"
try:
with open(output_path, "wb") as output_file:
for part_path in parts:
with open(part_path, "rb") as part_file:
while chunk := part_file.read(BUFFER_SIZE):
output_file.write(chunk)
os.remove(part_path)
return True
except Exception as e:
return False

def download_file(HOST, PORT, file_name, file_size):
global last_used_line_in_terminal
os.makedirs(DOWNLOAD_FOLDER, exist_ok=True)

threads = []
parts = []

base_line = last_used_line_in_terminal + 1
last_used_line_in_terminal = base_line + 6

print(f"\033[{base_line}HDownloading {file_name}...\n")

for i in range(4):
start = i * (file_size // 4)
end = file_size if i == 3 else (i + 1) * (file_size // 4)

part_size = end - start

thread = threading.Thread(target=download_part, args=(HOST, PORT, file_name, i + 1, start, end, base_line, part_size))
threads.append(thread)
thread.start()

for thread in threads:
thread.join()

for i in range(4):
part_path = f"./{DOWNLOAD_FOLDER}/{file_name}.part{i + 1}"
if not os.path.exists(part_path):
return False
parts.append(part_path)

success = merge_parts(file_name, parts)
try:
merged_file_size = os.path.getsize(f"./{DOWNLOAD_FOLDER}/{file_name}")
if success and merged_file_size == file_size:
print(f"\033[{base_line + 5}HDownload {file_name} completed successfully!\n")
print(f"\033[{base_line + 6}H" + "-" * 40)
else:
print(f"\033[{base_line + 5}HDownload {file_name} errors! Expected:{file_size}, Got:{merged_file_size}\n")
except FileNotFoundError:
print(f"\033[{base_line + 5}HDownload of {file_name} failed! Merged file not found.")
return success

Вот мой клиентский и серверный код. Он будет загружать файлы с сервера клиенту. Клиент создаст 4 параллельных соединения с сервером, каждое соединение будет отвечать за загрузку части исходного размера файла (при этом исходный размер файла разделен на 4), и мне нужно отобразить их прогресс на экране. .
Теперь вопросы, которые я хочу задать.
  • Я использовал Escape-код ANSI, чтобы отобразить прогресс, перемещая курсор, удаление старого номера и запись нового номера, это работает довольно хорошо, но если терминалу не хватает длины, он будет перезаписан в последней строке так же, как и на терминале. Я хочу посмотреть, есть ли способ отобразить еще 4 потока или исправить эту ошибку.

Код: Выделить всё

example display process
Host Name: 127.0.0.1
Available files:
genshin.png|5980541
arya.gif|3707944
jinx.png|2445272
alime.jpeg|23765
avt.jpeg|51573

Downloading arya.gif...
Part 1 - Progress: 100.00% / 100%
Part 2 - Progress: 100.00% / 100%
Part 3 - Progress: 100.00% / 100%
Part 4 - Progress: 100.00% / 100%
Download completed successfully for arya.gif!
----------------------------------------
Downloading jinx.png...
Part 1 - Progress: 100.00% / 100%
Part 2 - Progress: 100.00% / 100%
Part 3 - Progress: 100.00% / 100%
Part 4 - Progress: 100.00% / 100%
Download completed successfully for jinx.png!
  • Я использовал библиотеку журналов Python для проверки байтов, передаваемых между сервером и клиентом, и обнаружил, что они, похоже, не находятся на в то же время. В частности, следующим образом:

Код: Выделить всё

2024-12-15 00:33:37,379 - INFO - Received: 2260992 bytes in part 1 of vangoh.jpg
2024-12-15 00:33:37,387 - INFO - Received: 2342912 bytes in part 1 of vangoh.jpg
2024-12-15 00:33:37,395 - INFO - Received: 2424832 bytes in part 1 of vangoh.jpg
2024-12-15 00:33:37,396 - INFO - Received: 81816 bytes in part 2 of vangoh.jpg
2024-12-15 00:33:37,404 - INFO - Received: 2506752 bytes in part 1 of vangoh.jpg
2024-12-15 00:33:37,412 - INFO - Received: 163736 bytes in part 2 of vangoh.jpg
2024-12-15 00:33:37,420 - INFO - Received: 2588672 bytes in part 1 of vangoh.jpg
Это часть процесса передачи байтов, и когда часть 1 передаст около 80%, часть 2 начнет передачу, при этом я хочу, чтобы все 4 части передавали параллельно одновременно время (разница может быть, но она небольшая), из-за этого мой прогресс иногда отображает часть 1 примерно на 80/100% до того, как начинает загружаться часть 2. Я хочу посмотреть, как исправить эту ошибку.
Возможно, статья немного длинная, но я надеюсь, что все меня поддержат, не забудьте сохранить мои функции без изменений. Большое спасибо всем.
Я пробовал распечатать прогресс, но если терминалу недостаточно длины, он будет перезаписан в конце.
Я также пробовал загрузить файл, разделив его его на 4 параллельные части, но 4 потока не выполняются одновременно, но иногда часть 2 появляется перед частью 1, я хочу, чтобы все 4 запускались одновременно.

Подробнее здесь: https://stackoverflow.com/questions/792 ... r-file-soc
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «Python»