diff --git a/TFTPServer.py b/TFTPServer.py index f01bd89..6d74095 100644 --- a/TFTPServer.py +++ b/TFTPServer.py @@ -224,6 +224,8 @@ class TFTPServer: Передача файла клиенту по протоколу TFTP. """ BLOCK_SIZE = 512 + MAX_RETRIES = 5 + TIMEOUT = 2.0 transfer_socket = None try: if not os.path.exists(file_path): @@ -249,124 +251,99 @@ class TFTPServer: 'start_time': start_time } - self.log(f"[INFO] Начало передачи файла '{file_basename}' клиенту {client_addr}. Размер файла: {filesize} байт.") - - # Создаем отдельный сокет для передачи файла + # Создаем новый сокет для передачи данных transfer_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - transfer_socket.settimeout(5.0) # Таймаут ожидания ACK + transfer_socket.settimeout(TIMEOUT) - # Добавляем сокет в множество активных сокетов with self.lock: self.transfer_sockets.add(transfer_socket) - block_num = 1 - bytes_sent = 0 - last_progress_time = time.time() + self.log(f"[INFO] Начало передачи файла '{file_basename}' клиенту {client_addr}. Размер файла: {filesize} байт.") - try: - with open(file_path, "rb") as f: - while self.running: # Проверяем флаг running + with open(file_path, 'rb') as file: + block_number = 1 + last_successful_block = 0 + + while True: + # Читаем блок данных + data = file.read(BLOCK_SIZE) + + # Формируем и отправляем пакет данных + packet = struct.pack('!HH', 3, block_number) + data + + retries = 0 + while retries < MAX_RETRIES: try: - data_block = f.read(BLOCK_SIZE) - if not data_block: # Достигнут конец файла - break - - # Проверяем флаг running перед отправкой блока - if not self.running: - raise Exception("Передача прервана: сервер остановлен") - - # Формируем TFTP пакет данных - packet = struct.pack("!HH", 3, block_num) + data_block - attempts = 0 - ack_received = False - - # Попытка отправки текущего блока (до 3 повторных попыток) - while attempts < 3 and not ack_received and self.running: - if transfer_socket is None: - raise Exception("Сокет передачи закрыт") - + transfer_socket.sendto(packet, client_addr) + + # Ожидаем подтверждение + while True: try: - transfer_socket.sendto(packet, client_addr) - - # Логируем прогресс каждую секунду - current_time = time.time() - if current_time - last_progress_time >= 1.0: - elapsed_time = current_time - start_time - remaining = filesize - bytes_sent - self.log(f"[STATUS] Клиент: {client_addr} | Файл: {file_basename} | " - f"Отправлено: {bytes_sent}/{filesize} байт | " - f"Осталось: {remaining} байт | " - f"Время: {elapsed_time:.2f} сек.") - last_progress_time = current_time - - # Ожидаем подтверждение - ack_data, addr = transfer_socket.recvfrom(4) - if addr == client_addr: - ack_opcode, ack_block = struct.unpack("!HH", ack_data) - if ack_opcode == 4 and ack_block == block_num: - ack_received = True - bytes_sent += len(data_block) - with self.lock: - if client_addr in self.active_transfers: - self.active_transfers[client_addr]['bytes_sent'] = bytes_sent - else: - self.log(f"[WARN] Неверный ACK от {client_addr}. " - f"Ожидался блок {block_num}, получен {ack_block}.") + ack_data, ack_addr = transfer_socket.recvfrom(4) + if ack_addr == client_addr and len(ack_data) >= 4: + opcode, ack_block = struct.unpack('!HH', ack_data) + if opcode == 4: # ACK + if ack_block == block_number: + # Успешное подтверждение + last_successful_block = block_number + bytes_sent = min((block_number * BLOCK_SIZE), filesize) + + # Обновляем информацию о прогрессе + with self.lock: + if client_addr in self.active_transfers: + self.active_transfers[client_addr]['bytes_sent'] = bytes_sent + + # Логируем статус каждую секунду + current_time = time.time() + if current_time - start_time >= 1.0: + bytes_remaining = filesize - bytes_sent + elapsed_time = current_time - start_time + self.log(f"[STATUS] Клиент: {client_addr} | Файл: {file_basename} | " + f"Отправлено: {bytes_sent}/{filesize} байт | " + f"Осталось: {bytes_remaining} байт | " + f"Время: {elapsed_time:.2f} сек.") + + break + elif ack_block < block_number: + # Получен старый ACK, игнорируем + continue except socket.timeout: - attempts += 1 - self.log(f"[WARN] Таймаут ожидания ACK для блока {block_num} " - f"от {client_addr}. Попытка {attempts+1}.") - except socket.error as e: - if not self.running: - raise Exception("Передача прервана: сервер остановлен") - self.log(f"[ERROR] Ошибка сокета при отправке блока {block_num}: {str(e)}") - attempts += 1 - except Exception as e: - if not self.running: - raise Exception("Передача прервана: сервер остановлен") - self.log(f"[ERROR] Ошибка при отправке блока {block_num}: {str(e)}") - attempts += 1 - - if not ack_received: - raise Exception(f"Не удалось получить подтверждение для блока {block_num}") - - block_num = (block_num + 1) % 65536 - + break + + if last_successful_block == block_number: + break + else: + retries += 1 + self.log(f"[WARN] Таймаут ожидания ACK для блока {block_number} от {client_addr}. " + f"Попытка {retries + 1}.") except Exception as e: - if not self.running: - raise Exception("Передача прервана: сервер остановлен") - raise + retries += 1 + self.log(f"[ERROR] Ошибка при передаче блока {block_number}: {str(e)}") + + if retries >= MAX_RETRIES: + self.log(f"[ERROR] Превышено максимальное количество попыток для блока {block_number}") + return + + block_number += 1 + + # Если отправили меньше BLOCK_SIZE байт, это был последний блок + if len(data) < BLOCK_SIZE: + break - if bytes_sent == filesize: - elapsed_time = time.time() - start_time - self.log(f"[INFO] Передача файла '{file_basename}' клиенту {client_addr} " - f"завершена за {elapsed_time:.2f} сек. Всего отправлено {bytes_sent} байт.") - except Exception as e: - if not self.running: - self.log(f"[INFO] Передача файла '{file_basename}' клиенту {client_addr} прервана: сервер остановлен") - raise + self.log(f"[INFO] Передача файла '{file_basename}' клиенту {client_addr} завершена успешно") except Exception as e: - if not self.running: - return # Не логируем повторно о прерывании передачи - self.log(f"[ERROR] Ошибка при передаче файла '{os.path.basename(file_path)}' " - f"клиенту {client_addr}: {str(e)}") - try: - self.send_error(client_addr, 0, str(e)) - except: - pass + self.log(f"[ERROR] Ошибка при передаче файла: {str(e)}") finally: - # Закрываем сокет передачи - if transfer_socket: - try: - with self.lock: - self.transfer_sockets.discard(transfer_socket) - transfer_socket.close() - transfer_socket = None - except: - pass - - # Удаляем информацию о передаче + # Очищаем информацию о передаче with self.lock: if client_addr in self.active_transfers: - del self.active_transfers[client_addr] \ No newline at end of file + del self.active_transfers[client_addr] + if transfer_socket in self.transfer_sockets: + self.transfer_sockets.remove(transfer_socket) + + if transfer_socket: + try: + transfer_socket.close() + except: + pass \ No newline at end of file