325 lines
8.1 KiB
Python
325 lines
8.1 KiB
Python
"""
|
||
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Проверка пройдена: "
|
||
"все сообщения учтены."
|
||
) |