ИУП: интенсив 7 дней + диагностический пакет (32 вопроса, 9 задач с автопроверкой)

This commit is contained in:
Kodlo-chan
2026-09-23 16:05:09 +07:00
commit 4cd63b3d4d
40 changed files with 2464 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
// Задача 07 — TCP-эхо-сервер на epoll. Реализуй сервер.
// Запуск: ./solution <порт>
#include <cstdio>
#include <cstdlib>
int main(int argc, char** argv) {
if (argc < 2) {
std::fprintf(stderr, "usage: %s <port>\n", argv[0]);
return 2;
}
long port = std::strtol(argv[1], nullptr, 10);
std::fprintf(stderr, "TODO: сервер на порту %ld не реализован\n", port);
return 1; // TODO: реализовать сервер на epoll
}
+17
View File
@@ -0,0 +1,17 @@
# Задача 07 — TCP-эхо-сервер на epoll (Linux, C++)
Написать сервер: принимает TCP-соединения на `127.0.0.1:<порт из argv[1]>`, эхо всех
принятых байт обратно в то же соединение, обслуживает несколько клиентов одновременно,
завершается по SIGTERM/SIGINT аккуратно (exit 0).
Требования:
- мультиплексирование через `epoll` (не блокирующий `accept` в цикле, не поток на клиента);
- сокеты неблокирующие, обработка `EAGAIN`/`EINTR`, частичные чтения и записи;
- `SO_REUSEADDR`, bind на 127.0.0.1, прослушивание на нужном порту;
- до 64 одновременных соединений, закрытие по EOF со стороны клиента;
- никаких утечек дескрипторов (тест проверяет закрытие).
Запускается как самостоятельный бинарник: `./solution <порт>`.
Проверка: `python3 grade.py 07` — тест сам поднимает сервер, открывает 3 соединения,
льёт данные, проверяет эхо и корректное завершение по SIGTERM.
Критерий: все `ok` + ревью кода (использование epoll, обработка ошибок).
+118
View File
@@ -0,0 +1,118 @@
#!/usr/bin/env python3
"""Тест задачи 07: поднимает сервер, проверяет эхо на нескольких соединениях и SIGTERM."""
import os
import signal
import socket
import subprocess
import sys
import time
HERE = os.path.dirname(os.path.abspath(__file__))
BIN = os.path.join(HERE, "solution")
failures = 0
def check(cond, name):
global failures
if cond:
print(f"ok {name}")
else:
print(f"FAIL {name}")
failures += 1
def free_port():
s = socket.socket()
s.bind(("127.0.0.1", 0))
p = s.getsockname()[1]
s.close()
return p
def main():
port = free_port()
proc = subprocess.Popen([BIN, str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
up = False
for _ in range(100): # до 5 секунд на старт
if proc.poll() is not None:
break
try:
s = socket.create_connection(("127.0.0.1", port), timeout=0.3)
s.close()
up = True
break
except OSError:
time.sleep(0.05)
check(up, "сервер принял соединение и слушает порт")
if up:
# три одновременных соединения, большие и разорванные сообщения
socks = []
for i in range(3):
s = socket.create_connection(("127.0.0.1", port), timeout=2)
s.settimeout(5)
socks.append(s)
payloads = [b"hello\n", b"x" * 70000, b"chunk-" + bytes(range(32, 127))]
for s, p in zip(socks, payloads):
s.sendall(p)
ok = True
for i, (s, p) in enumerate(zip(socks, payloads)):
got = b""
while len(got) < len(p):
chunk = s.recv(65536)
if not chunk:
break
got += chunk
if got != p:
ok = False
print(f" соединение {i}: получено {len(got)} из {len(p)} байт")
check(ok, "эхо на трёх одновременных соединениях (включая 70 КБ)")
# эхо в двух шагах по одному соединению
s = socks[0]
s.sendall(b"part1")
first = s.recv(5)
s.sendall(b"part2")
second = b""
while len(second) < 5:
chunk = s.recv(5 - len(second))
if not chunk:
break
second += chunk
check(first == b"part1" and second == b"part2", "эхо по частям (несколько циклов read/write)")
# клиент закрывается — сервер не падает
for s in socks:
s.close()
time.sleep(0.3)
check(proc.poll() is None, "сервер жив после закрытия всех клиентов")
# новое соединение после закрытия старых
try:
s = socket.create_connection(("127.0.0.1", port), timeout=2)
s.settimeout(3)
s.sendall(b"again")
check(s.recv(5) == b"again", "новое соединение после отключения старых")
s.close()
except OSError as e:
check(False, f"новое соединение после отключения старых ({e})")
proc.send_signal(signal.SIGTERM)
try:
rc = proc.wait(timeout=5)
check(rc == 0, f"аккуратное завершение по SIGTERM (exit={rc})")
except subprocess.TimeoutExpired:
proc.kill()
check(False, "аккуратное завершение по SIGTERM (не завершился за 5 с)")
if failures:
err = proc.stderr.read().decode(errors="replace")[:2000]
if err:
print("--- stderr сервера ---")
print(err)
print(f"\n{'FAILURES: %d' % failures if failures else 'ALL PASS'}")
sys.exit(1 if failures else 0)
if __name__ == "__main__":
main()