Перейти к содержанию

ЛР 1. Сокеты

1. Задание 1: Взаимодействие по протоколу UDP

Цель: Разработать простейший клиент-серверный сервис для демонстрации передачи данных в формате дейтаграмм с использованием протокола UDP (User Datagram Protocol).

Описание: Реализация включала создание двух скриптов (task1_server.py и task1_client.py). Ключевой особенностью стало использование сокета типа SOCK_DGRAM и методов sendto() и recvfrom(). Эти методы позволили клиенту отправить сообщение на заданный адрес сервера, не требуя предварительного установления соединения. Сервер принял дейтаграмму, определил адрес отправителя и отправил ответную дейтаграмму, используя этот адрес.

Вывод: Работа успешно продемонстрировала принцип функционирования протокола UDP: быстрая, не требующая подтверждения и не гарантирующая доставку связь, идеальная для приложений, где важна скорость, а не надежность.


2. Задание 2: Решение квадратного уравнения по TCP

Цель: Разработать надежный клиент-серверный сервис, использующий протокол TCP, для вычисления корней квадратного уравнения (ax^2 + bx + c = 0) на стороне сервера.

Описание: Использовался сокет типа SOCK_STREAM. Клиент устанавливал соединение методом s.connect(), отправлял строку с коэффициентами. Сервер принимал соединение методом s.accept(), парсил входную строку и вызывал функцию для решения уравнения. Функция solve_quadratic_min обрабатывала все случаи дискриминанта ($D > 0$, $D = 0$, $D < 0$), используя модуль cmath для работы с комплексными корнями. Результат надежно передавался обратно клиенту по установленному соединению.

Вывод: Сервис подтвердил ключевые свойства TCP: надежность и упорядоченность передачи данных, что необходимо для точных вычислений.


3. Задание 3: Базовый HTTP GET-сервер

Цель: Реализовать простейший Web-сервер, способный обрабатывать HTTP-запросы методом GET.

Описание: Сервер был реализован на базе TCP-сокета и прослушивал порт 8080. После приема соединения и получения HTTP-запроса, сервер вручную формировал полный HTTP-ответ. Этот ответ включал статус HTTP/1.1 200 OK, необходимые заголовки (Content-Type: text/html, Content-Length) и содержимое статического HTML-файла (index3.html).

Вывод: Успешно продемонстрировано, что протокол HTTP является текстовым протоколом прикладного уровня, работающим поверх надежного транспортного протокола TCP.


Вот развернутый отчет для Заданий 4 и 5 с листингами кода. Формат оптимизирован для копирования в ваш .md файл.


Задание 4: Многопоточный чат-сервер (TCP)

Цель: Разработать TCP-сервер, способный одновременно обслуживать множество клиентов, реализуя функционал группового чата с мгновенной рассылкой сообщений.

Описание реализации: Основная сложность чат-сервера заключается в необходимости одновременно слушать новых клиентов и принимать сообщения от уже подключенных. Для этого была использована многопоточность (модуль threading):

  1. Слушающий поток: Основной цикл программы ожидает новые подключения через accept().
  2. Рабочие потоки: Для каждого клиента создается индивидуальный поток, который в бесконечном цикле ждет данные именно от этого пользователя.
  3. Broadcasting: При получении сообщения сервер пересылает его всем активным участникам, список которых хранится в глобальном массиве CLIENTS.

Листинг кода (Сервер)

import socket
import threading

HOST = '127.0.0.1'
PORT = 9090
CLIENTS = []

def broadcast(message, sender_socket):
    for client in CLIENTS:
        if client != sender_socket:
            try:
                client.send(message)
            except:
                client.close()
                if client in CLIENTS: CLIENTS.remove(client)

def handle_client(client_socket, addr):
    print(f"[NEW CONNECTION] {addr} connected.")
    try:
        while True:
            message = client_socket.recv(1024)
            if not message: break
            print(f"[{addr}] {message.decode('utf-8')}")
            broadcast(message, client_socket)
    finally:
        CLIENTS.remove(client_socket)
        client_socket.close()

def start_chat():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind((HOST, PORT))
    server.listen()
    print(f"[LISTENING] Server is on {HOST}:{PORT}")

    while True:
        conn, addr = server.accept()
        CLIENTS.append(conn)
        thread = threading.Thread(target=handle_client, args=(conn, addr))
        thread.start()

if __name__ == "__main__":
    start_chat()

Вывод: Использование многопоточности позволило избежать блокировки сервера одним клиентом. Протокол TCP обеспечил гарантию того, что сообщения в чате не потеряются и придут в правильном порядке.


Задание 5: Web-сервер с обработкой форм (HTTP POST)

Цель: Реализовать HTTP-сервер, поддерживающий метод POST для приема данных от пользователя и динамическое обновление содержимого страницы.

Описание реализации: В отличие от статического сервера, данная программа анализирует HTTP-заголовки запроса:

  1. Метод GET: Сервер просто отдает HTML-страницу с формой.
  2. Метод POST: Сервер считывает тело запроса (Body), которое следует после пустой строки \r\n\r\n. Данные извлекаются в формате key=value и парсятся с помощью urllib.parse.parse_qs.
  3. Динамика: Вместо создания новой страницы, сервер берет заготовку (шаблон) и заменяет в ней метку-заполнитель на сгенерированный "на лету" HTML-список с данными.

Листинг кода (Web-сервер)

import socket
from urllib.parse import parse_qs

HOST = '127.0.0.1'
PORT = 8081
# Простейшая база данных в памяти
DATA_STORAGE = []

HTML_TEMPLATE = """
<html>
<head><meta charset="utf-8"><title>Задание 5</title></head>
<body>
    <form method="POST">
        Предмет: <input name="subject"> Оценка: <input name="grade">
        <button type="submit">Добавить</button>
    </form>
    <h2>Список оценок:</h2>
    <ul>{content}</ul>
</body>
</html>
"""

def run_web_server():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind((HOST, PORT))
        s.listen(5)
        print(f"Web-server running on http://{HOST}:{PORT}")

        while True:
            conn, addr = s.accept()
            request = conn.recv(4096).decode('utf-8')
            if not request: continue

            lines = request.split('\n')
            method = lines[0].split()[0]

            if method == 'POST':
                # Извлекаем тело запроса
                body = request.split('\r\n\r\n')[1]
                params = parse_qs(body)
                subj = params.get('subject', [''])[0]
                grade = params.get('grade', [''])[0]
                DATA_STORAGE.append(f"{subj}: {grade}")

            # Рендеринг страницы
            list_html = "".join([f"<li>{item}</li>" for item in DATA_STORAGE])
            response_body = HTML_TEMPLATE.format(content=list_html)

            response = f"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n{response_body}"
            conn.sendall(response.encode('utf-8'))
            conn.close()

if __name__ == "__main__":
    run_web_server()

Вывод: Реализована базовая логика веб-приложения: сервер научился не только "отдавать" данные, но и "принимать" их, изменяя свое состояние (хранилище данных) и отображая актуальную информацию пользователю.