ЛР 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):
- Слушающий поток: Основной цикл программы ожидает новые подключения через
accept(). - Рабочие потоки: Для каждого клиента создается индивидуальный поток, который в бесконечном цикле ждет данные именно от этого пользователя.
- 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-заголовки запроса:
- Метод GET: Сервер просто отдает HTML-страницу с формой.
- Метод POST: Сервер считывает тело запроса (Body), которое следует после пустой строки
\r\n\r\n. Данные извлекаются в форматеkey=valueи парсятся с помощьюurllib.parse.parse_qs. - Динамика: Вместо создания новой страницы, сервер берет заготовку (шаблон) и заменяет в ней метку-заполнитель на сгенерированный "на лету" 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()
Вывод: Реализована базовая логика веб-приложения: сервер научился не только "отдавать" данные, но и "принимать" их, изменяя свое состояние (хранилище данных) и отображая актуальную информацию пользователю.