""" Lab006. Надёжная передача пакетов методом Stop-and-Wait ARQ. Передатчик: 1. Отправляет пакет. 2. Ожидает подтверждение ACK. 3. При отсутствии правильного ACK повторяет передачу. 4. После исчерпания попыток считает сообщение недоставленным. Приёмник: 1. Проверяет пакет. 2. Отбрасывает повреждённые пакеты. 3. Отправляет ACK для правильного пакета. 4. Не передаёт приложению повторные копии одного сообщения. """ import random from protocol.packet import ( CRCError, MESSAGE_TYPE_ACK, MESSAGE_TYPE_TEXT, PacketError, build_packet, parse_packet, ) from tools.virtual_channel import transmit # ============================================================ # Настройки эксперимента # ============================================================ MESSAGE_COUNT = 100 # Максимальное количество передач одного сообщения. MAX_ATTEMPTS = 5 # Параметры канала от передатчика к приёмнику. DATA_DROP_PROBABILITY = 0.10 DATA_BIT_ERROR_PROBABILITY = 0.002 # Параметры обратного канала для ACK. ACK_DROP_PROBABILITY = 0.10 ACK_BIT_ERROR_PROBABILITY = 0.002 # Фиксированное число делает эксперимент повторяемым. random_generator = random.Random(2026) # ============================================================ # Счётчики # ============================================================ delivered_messages = 0 failed_messages = 0 data_transmissions = 0 ack_transmissions = 0 duplicate_packets = 0 invalid_data_packets = 0 invalid_ack_packets = 0 dropped_data_packets = 0 dropped_ack_packets = 0 # Номера пакетов, уже переданных приложению приёмника. received_sequences = set() # ============================================================ # Передача сообщений # ============================================================ for sequence_number in range(MESSAGE_COUNT): message = f"ПАКЕТ {sequence_number}" payload = message.encode("utf-8") data_packet = build_packet( payload=payload, message_type=MESSAGE_TYPE_TEXT, sequence_number=sequence_number, ) message_confirmed = False # Повторяем передачу до MAX_ATTEMPTS раз. for attempt_number in range(1, MAX_ATTEMPTS + 1): data_transmissions += 1 received_data_packet, _ = transmit( packet=data_packet, drop_probability=DATA_DROP_PROBABILITY, bit_error_probability=DATA_BIT_ERROR_PROBABILITY, random_generator=random_generator, ) # Пакет полностью потерялся. if received_data_packet is None: dropped_data_packets += 1 continue # Приёмник проверяет пакет. try: parsed_data = parse_packet(received_data_packet) except (CRCError, PacketError): invalid_data_packets += 1 continue # Проверка типа сообщения. if parsed_data.message_type != MESSAGE_TYPE_TEXT: invalid_data_packets += 1 continue # Проверяем, не получали ли мы этот пакет ранее. if parsed_data.sequence_number in received_sequences: duplicate_packets += 1 else: restored_message = parsed_data.payload.decode("utf-8") assert restored_message == message received_sequences.add( parsed_data.sequence_number ) # Формируем подтверждение. # # ACK имеет тот же sequence_number, # что и подтверждаемый пакет. ack_packet = build_packet( payload=b"", message_type=MESSAGE_TYPE_ACK, sequence_number=parsed_data.sequence_number, ) ack_transmissions += 1 received_ack_packet, _ = transmit( packet=ack_packet, drop_probability=ACK_DROP_PROBABILITY, bit_error_probability=ACK_BIT_ERROR_PROBABILITY, random_generator=random_generator, ) # ACK полностью потерялся. if received_ack_packet is None: dropped_ack_packets += 1 continue # Передатчик проверяет ACK. try: parsed_ack = parse_packet(received_ack_packet) except (CRCError, PacketError): invalid_ack_packets += 1 continue # ACK должен иметь правильный тип и номер. if ( parsed_ack.message_type != MESSAGE_TYPE_ACK or parsed_ack.sequence_number != sequence_number ): invalid_ack_packets += 1 continue # Подтверждение получено. message_confirmed = True break if message_confirmed: delivered_messages += 1 else: failed_messages += 1 # ============================================================ # Расчёт статистики # ============================================================ delivery_rate = ( delivered_messages / MESSAGE_COUNT * 100 ) failure_rate = ( failed_messages / MESSAGE_COUNT * 100 ) average_data_transmissions = ( data_transmissions / MESSAGE_COUNT ) # ============================================================ # Вывод результата # ============================================================ print("=== Lab006. Stop-and-Wait ARQ ===") print("\nНастройки:") print("Количество сообщений:", MESSAGE_COUNT) print("Максимум попыток:", MAX_ATTEMPTS) print( "Вероятность потери DATA:", DATA_DROP_PROBABILITY, ) print( "Вероятность битовой ошибки DATA:", DATA_BIT_ERROR_PROBABILITY, ) print( "Вероятность потери ACK:", ACK_DROP_PROBABILITY, ) print( "Вероятность битовой ошибки ACK:", ACK_BIT_ERROR_PROBABILITY, ) print("\nОсновные результаты:") print( "Успешно подтверждено сообщений:", delivered_messages, ) print( "Не подтверждено сообщений:", failed_messages, ) print( f"Доля успешной доставки: " f"{delivery_rate:.1f} %" ) print( f"Доля недоставленных сообщений: " f"{failure_rate:.1f} %" ) print("\nПередачи:") print( "Всего передач DATA:", data_transmissions, ) print( "Всего передач ACK:", ack_transmissions, ) print( f"Среднее число передач DATA " f"на одно сообщение: " f"{average_data_transmissions:.2f}" ) print("\nОшибки канала:") print( "Полностью потеряно DATA:", dropped_data_packets, ) print( "Повреждено DATA:", invalid_data_packets, ) print( "Полностью потеряно ACK:", dropped_ack_packets, ) print( "Повреждено ACK:", invalid_ack_packets, ) print( "Получено повторных DATA-пакетов:", duplicate_packets, ) # ============================================================ # Автоматические проверки # ============================================================ assert ( delivered_messages + failed_messages == MESSAGE_COUNT ) assert delivered_messages <= len(received_sequences) assert data_transmissions >= MESSAGE_COUNT print( "\nПроверка пройдена: " "все сообщения учтены." )