48. (П21) Блокуючий ехо-сервер на сокетах¶
Передумови¶
- Прочитана Лекція 46 — Основи сокетів та блокуючий режим роботи
- Прочитана Лекція 47 — Обмеження блокуючих сокетів та підходи до їх подолання
- Python 3.10+
- Утиліта
nc(netcat) — є за замовчуванням у більшості Linux-дистрибутивів та macOS
Персоналізація
У кожному файлі оголосіть константу STUDENT = "Imya Prizvysche" (ваші дані латиницею) і виводьте її у першому рядку кожного запуску програми: print(f"Student: {STUDENT}"). Окрім того, сервер надсилає це ім'я у вітальному повідомленні кожному клієнту — тому на скріншотах звіту воно має бути видно і на стороні сервера, і на стороні клієнта.
Завдання¶
Реалізувати блокуючий TCP ехо-сервер, який, окрім простого повторення повідомлень, розуміє кілька керуючих команд і веде статистику для кожного з'єднання.
Сервер слухає на 127.0.0.1:9100, використовує SO_REUSEADDR. Кожне повідомлення від клієнта — це один виклик recv (на практиці у nc це один рядок, бо stdin лінійно-буферизовано).
Файл echo_server.py.
При підключенні клієнта сервер веде per-connection лічильник отриманих повідомлень n (з 1) і запам'ятовує момент підключення started_at = time.monotonic().
Одразу після accept сервер надсилає клієнту вітання:
Далі у циклі читайте від клієнта recv(4096). Якщо повернулось b"" — клієнт закрив з'єднання, виходимо з внутрішнього циклу. Інакше декодуйте байти через decode() і обріжте пробіли по краях — це і буде msg. Залежно від msg.upper() сервер реагує так:
| Команда | Відповідь |
|---|---|
PING |
PONG |
TIME |
поточний час у форматі ISO-8601 — datetime.datetime.now().isoformat(timespec="seconds") |
WHOAMI |
<ip>:<port> клієнта (з кортежу addr, що повернув accept) |
STATS |
messages: <n>, uptime: <s>s, де n — поточне значення лічильника, s — int(time.monotonic() - started_at) |
QUIT |
надіслати BYE і закрити з'єднання (break із внутрішнього циклу) |
| будь-що інше | [<n>] <msg> — ехо з префіксом-номером повідомлення |
Лічильник n інкрементується на кожне отримане повідомлення, незалежно від того, команда це чи звичайний текст. Вітання відправляється до першого recv і у лічильнику не враховується.
На стороні сервера на кожну подію друкуйте один рядок логу:
connected: <ip>:<port>— післяaccept;<- <ip>:<port>: <msg>— отримано повідомлення;-> <ip>:<port>: <reply>— надіслано відповідь;disconnected: <ip>:<port> (n=<n>, uptime=<s>s)— після виходу з внутрішнього циклу.
Не забудьте:
setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)передbind;- слухаючий сокет — у
with socket.socket(...) as server:, а сокет з'єднання —with conn:, щоб гарантовано закривалися навіть на винятках; - зовнішній
try/except KeyboardInterruptу головному циклі — щоб післяCtrl+Cнадрукуватиserver stoppedі завершитись штатно.
Запустіть сервер. Перевірте всі команди через nc 127.0.0.1 9100. Очікуваний приклад сесії клієнта:
$ nc 127.0.0.1 9100
Hello from Imya Prizvysche! Commands: PING | TIME | WHOAMI | STATS | QUIT
PING
PONG
hello world
[2] hello world
TIME
2026-05-05T14:33:02
STATS
messages: 4, uptime: 7s
QUIT
BYE
Зверніть увагу: ехо hello world отримало префікс [2], а не [1] — бо PING був першим повідомленням і вже інкрементував лічильник.
Результат¶
Після виконання роботи здайте звіт зі скріншотом сесії з nc, де видно вітання з вашим ім'ям та виконано всі п'ять команд (PING, TIME, WHOAMI, STATS, QUIT); поряд — лог сервера зі своїм Student: ... та подіями.
Знайшли помилку чи бажаєте додати інформацію, щоб покращити курс? Створіть issue на GitHub