#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Модуль для TFTP-сервера. """ import socket import threading import os import time class TFTPServerThread(threading.Thread): def __init__(self, host, port, log_callback): super().__init__() self.host = host self.port = port self.log_callback = log_callback self.running = True self.sock = None def run(self): try: self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.bind((self.host, self.port)) self.log_callback(f"TFTP сервер запущен на {self.host}:{self.port}\n") except Exception as e: self.log_callback(f"Ошибка запуска TFTP сервера: {e}\n") return while self.running: try: self.sock.settimeout(1) data, addr = self.sock.recvfrom(1024) if data: self.handle_rrq(data, addr) except socket.timeout: continue except Exception as e: self.log_callback(f"Ошибка: {e}\n") if self.sock: self.sock.close() self.log_callback("TFTP сервер остановлен.\n") def handle_rrq(self, data, addr): opcode = int.from_bytes(data[0:2], byteorder='big') if opcode != 1: self.log_callback(f"Получен не RRQ запрос от {addr}\n") return parts = data[2:].split(b'\0') if len(parts) < 2: self.log_callback(f"Неверный формат RRQ от {addr}\n") return req_filename = parts[0].decode('utf-8', errors='ignore') mode = parts[1].decode('utf-8', errors='ignore') self.log_callback(f"Получен RRQ для файла '{req_filename}' (режим {mode}) от {addr}\n") filepath = os.path.join("Firmware", req_filename) if not os.path.exists(filepath): self.log_callback(f"Файл '{req_filename}' не найден в папке Firmware\n") return try: filesize = os.path.getsize(filepath) f = open(filepath, 'rb') except Exception as e: self.log_callback(f"Ошибка открытия файла '{req_filename}': {e}\n") return block_num = 1 bytes_sent = 0 start_time = time.time() while True: block_data = f.read(512) data_packet = b'\x00\x03' + block_num.to_bytes(2, byteorder='big') + block_data self.sock.sendto(data_packet, addr) self.log_callback(f"Отправлен блок {block_num} ({len(block_data)} байт)\n") try: self.sock.settimeout(5) ack, ack_addr = self.sock.recvfrom(1024) if ack_addr != addr: continue ack_opcode = int.from_bytes(ack[0:2], byteorder='big') ack_block = int.from_bytes(ack[2:4], byteorder='big') if ack_opcode != 4 or ack_block != block_num: self.log_callback(f"Неверный ACK от {addr}\n") break except socket.timeout: self.log_callback("Таймаут ожидания ACK\n") break bytes_sent += len(block_data) elapsed = time.time() - start_time speed = bytes_sent / elapsed if elapsed > 0 else 0 progress = (bytes_sent / filesize) * 100 if filesize > 0 else 0 self.log_callback(f"Прогресс: {progress:.2f}% | Скорость: {speed:.2f} байт/с\n") if len(block_data) < 512: break block_num += 1 f.close() def stop(self): self.running = False