commit e66d4fa927e2397875270fd867e9e01fcb1179e0 Author: patayatuya-art Date: Thu May 7 15:00:51 2026 +0300 Add lab3 network examples diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ff36c5e --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.venv/ +__pycache__/ +*.pyc +.env +.idea/ +.vscode/ + diff --git a/README.md b/README.md new file mode 100644 index 0000000..67ba4c5 --- /dev/null +++ b/README.md @@ -0,0 +1,130 @@ +# Лабораторная работа 3 + +Тема: работа с сетевыми соединениями в Python. + +В проекте реализованы простые примеры TCP, UDP, HTTP-запросов через `socket` и `requests`, а также пример запроса к API Gitea. + +## Подготовка + +Создать и активировать виртуальное окружение: + +```powershell +python -m venv .venv +.\.venv\Scripts\Activate.ps1 +``` + +Установить зависимости: + +```powershell +pip install -r requirements.txt +``` + +## TCP + +Сервер: + +```powershell +python tcp_server.py +``` + +Клиент: + +```powershell +python tcp_client.py "hello server" +``` + +Чтобы остановить TCP-сервер: + +```powershell +python tcp_client.py EXIT +``` + +TCP-сервер работает на порту `10000`. Он принимает сообщение, переводит его в верхний регистр и возвращает клиенту. + +Фильтр Wireshark: + +```text +tcp.port==10000 +``` + +## UDP + +Сервер: + +```powershell +python udp_server.py +``` + +Клиент: + +```powershell +python udp_client.py "hello server" +``` + +Чтобы остановить UDP-сервер: + +```powershell +python udp_client.py EXIT +``` + +UDP-сервер работает на порту `10001`. Он принимает сообщение, переворачивает строку и возвращает клиенту. + +Фильтр Wireshark: + +```text +udp.port==10001 +``` + +## HTTP через socket + +```powershell +python http_socket_client.py +``` + +Программа вручную открывает TCP-соединение с `vyatsu.ru` на порту `80`, отправляет HTTP-запрос и выводит часть ответа. +При проверке сервер вернул ответ `301 Moved Permanently`, потому что сайт перенаправляет запрос на HTTPS-версию. + +Фильтр Wireshark: + +```text +tcp.port==80 +``` + +## HTTP через requests + +```powershell +python http_requests_client.py +``` + +`requests` сам формирует HTTP-запрос, обрабатывает ответ и предоставляет удобный объект `response`. +При проверке `requests` автоматически обработал перенаправление и получил итоговый статус `200`. + +## Gitea API + +Токен нельзя хранить в коде. Нужно создать файл `.env`: + +```text +GITEA_TOKEN=ваш_токен +``` + +Файл `.env` добавлен в `.gitignore`, поэтому он не должен попасть в коммит. + +Запуск: + +```powershell +python gitea_api.py +``` + +Программа выполняет запрос к: + +```text +https://git.vyatsu.ru/api/v1/user +``` + +## Выводы + +TCP устанавливает соединение перед передачей данных. В Wireshark можно увидеть трехстороннее рукопожатие: `SYN`, `SYN-ACK`, `ACK`. При завершении соединения видны пакеты `FIN` и `ACK`. + +UDP не устанавливает соединение перед отправкой данных. В Wireshark видны отдельные UDP-пакеты без handshake и без подтверждений доставки. + +При работе через `socket` HTTP-запрос нужно составлять вручную. В примере через `socket` виден ответ `301 Moved Permanently`. Библиотека `requests` делает больше действий автоматически: формирует заголовки, обрабатывает ответ и переходит по перенаправлению, поэтому итоговый ответ получился со статусом `200`. diff --git a/gitea_api.py b/gitea_api.py new file mode 100644 index 0000000..08adc9a --- /dev/null +++ b/gitea_api.py @@ -0,0 +1,42 @@ +import os + +import requests + + +API_URL = "https://git.vyatsu.ru/api/v1" + + +def load_token() -> str: + token = os.getenv("GITEA_TOKEN") + if token: + return token + + if os.path.exists(".env"): + with open(".env", "r", encoding="utf-8") as file: + for line in file: + line = line.strip() + if line.startswith("GITEA_TOKEN="): + return line.split("=", 1)[1].strip() + + return "" + + +token = load_token() +if not token: + print("Token not found. Create .env file with GITEA_TOKEN=your_token") + raise SystemExit(1) + +headers = {"Authorization": f"token {token}"} + +response = requests.get(f"{API_URL}/user", headers=headers, timeout=10) +print(f"Status code: {response.status_code}") + +if response.ok: + user = response.json() + print("Gitea user information:") + print(f"Login: {user.get('login')}") + print(f"Full name: {user.get('full_name')}") + print(f"Email: {user.get('email')}") +else: + print(response.text) + diff --git a/http_requests_client.py b/http_requests_client.py new file mode 100644 index 0000000..dc6c571 --- /dev/null +++ b/http_requests_client.py @@ -0,0 +1,8 @@ +import requests + + +response = requests.get("http://vyatsu.ru", timeout=10) + +print(f"Status code: {response.status_code}") +print(response.text[:500]) + diff --git a/http_socket_client.py b/http_socket_client.py new file mode 100644 index 0000000..8e5fced --- /dev/null +++ b/http_socket_client.py @@ -0,0 +1,33 @@ +import socket + + +HOST = "vyatsu.ru" +PORT = 80 + + +client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +client.connect((HOST, PORT)) + +request = ( + "GET / HTTP/1.1\r\n" + "Host: vyatsu.ru\r\n" + "User-Agent: python-socket-lab3\r\n" + "Accept: text/html\r\n" + "Connection: close\r\n" + "\r\n" +) + +client.sendall(request.encode("utf-8")) + +response_parts = [] +while True: + data = client.recv(4096) + if not data: + break + response_parts.append(data) + +client.close() + +response = b"".join(response_parts) +print(response.decode("utf-8", errors="replace")[:2000]) + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3288e92 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +requests + diff --git a/tcp_client.py b/tcp_client.py new file mode 100644 index 0000000..cbe640e --- /dev/null +++ b/tcp_client.py @@ -0,0 +1,21 @@ +import socket +import sys + + +HOST = "127.0.0.1" +PORT = 10000 + + +message = "hello server" +if len(sys.argv) > 1: + message = " ".join(sys.argv[1:]) + +client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +client.connect((HOST, PORT)) +client.sendall(message.encode("utf-8")) + +data = client.recv(1024) +print(f"Response from server: {data.decode('utf-8')}") + +client.close() + diff --git a/tcp_server.py b/tcp_server.py new file mode 100644 index 0000000..62575a8 --- /dev/null +++ b/tcp_server.py @@ -0,0 +1,39 @@ +import socket + + +HOST = "0.0.0.0" +PORT = 10000 + + +def process_message(message: str) -> str: + return f"TCP server received: {message.upper()}" + + +server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +server.bind((HOST, PORT)) +server.listen(1) + +print(f"TCP server started on port {PORT}") +print("Send EXIT from client to stop the server") + +while True: + conn, addr = server.accept() + print(f"Connection from {addr}") + + data = conn.recv(1024) + if not data: + conn.close() + continue + + message = data.decode("utf-8") + response = process_message(message) + conn.sendall(response.encode("utf-8")) + conn.close() + + if message.strip().upper() == "EXIT": + print("TCP server stopped") + break + +server.close() + diff --git a/udp_client.py b/udp_client.py new file mode 100644 index 0000000..0abdb2a --- /dev/null +++ b/udp_client.py @@ -0,0 +1,20 @@ +import socket +import sys + + +HOST = "127.0.0.1" +PORT = 10001 + + +message = "hello server" +if len(sys.argv) > 1: + message = " ".join(sys.argv[1:]) + +client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +client.sendto(message.encode("utf-8"), (HOST, PORT)) + +data, _ = client.recvfrom(1024) +print(f"Response from server: {data.decode('utf-8')}") + +client.close() + diff --git a/udp_server.py b/udp_server.py new file mode 100644 index 0000000..d45a60a --- /dev/null +++ b/udp_server.py @@ -0,0 +1,31 @@ +import socket + + +HOST = "0.0.0.0" +PORT = 10001 + + +def process_message(message: str) -> str: + return f"UDP server received: {message[::-1]}" + + +server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +server.bind((HOST, PORT)) + +print(f"UDP server started on port {PORT}") +print("Send EXIT from client to stop the server") + +while True: + data, addr = server.recvfrom(1024) + message = data.decode("utf-8") + print(f"Message from {addr}: {message}") + + response = process_message(message) + server.sendto(response.encode("utf-8"), addr) + + if message.strip().upper() == "EXIT": + print("UDP server stopped") + break + +server.close() +