Compare commits

...

3 Commits

Author SHA1 Message Date
patayatuya-art
a33e028581 Merge remote main 2026-05-07 17:31:17 +03:00
patayatuya-art
07ea43a69c Add Gitea repository creation example 2026-05-07 16:28:02 +03:00
patayatuya-art
e66d4fa927 Add lab3 network examples 2026-05-07 15:00:51 +03:00
11 changed files with 398 additions and 2 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.venv/
__pycache__/
*.pyc
.env
.idea/
.vscode/

141
README.md
View File

@ -1,3 +1,140 @@
# lab3 # Лабораторная работа 3
Repository created through Gitea API for lab 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
```
Для создания репозитория через API используется файл:
```powershell
python create_gitea_repo.py
```
Скрипт создает публичный репозиторий `API_REPO`.
## Выводы
TCP устанавливает соединение перед передачей данных. В Wireshark можно увидеть трехстороннее рукопожатие: `SYN`, `SYN-ACK`, `ACK`. При завершении соединения видны пакеты `FIN` и `ACK`.
UDP не устанавливает соединение перед отправкой данных. В Wireshark видны отдельные UDP-пакеты без handshake и без подтверждений доставки.
При работе через `socket` HTTP-запрос нужно составлять вручную. В примере через `socket` виден ответ `301 Moved Permanently`. Библиотека `requests` делает больше действий автоматически: формирует заголовки, обрабатывает ответ и переходит по перенаправлению, поэтому итоговый ответ получился со статусом `200`.
Через API Gitea был выполнен запрос к данным пользователя и создан репозиторий `API_REPO`. Для этого использовался токен, который хранится в `.env` и не попадает в коммиты.

56
create_gitea_repo.py Normal file
View File

@ -0,0 +1,56 @@
import os
import requests
API_URL = "https://git.vyatsu.ru/api/v1"
REPO_NAME = "API_REPO"
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}"}
data = {
"name": REPO_NAME,
"description": "Repository created through Gitea API for lab 3",
"private": False,
"auto_init": True,
}
response = requests.post(
f"{API_URL}/user/repos",
headers=headers,
json=data,
timeout=10,
)
print(f"Status code: {response.status_code}")
if response.status_code == 201:
repo = response.json()
print("Repository created")
print(f"Name: {repo.get('full_name')}")
print(f"URL: {repo.get('html_url')}")
elif response.status_code == 409:
print(f"Repository {REPO_NAME} already exists")
else:
print(response.text)

42
gitea_api.py Normal file
View File

@ -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)

8
http_requests_client.py Normal file
View File

@ -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])

33
http_socket_client.py Normal file
View File

@ -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])

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
requests

21
tcp_client.py Normal file
View File

@ -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()

39
tcp_server.py Normal file
View File

@ -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()

20
udp_client.py Normal file
View File

@ -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()

31
udp_server.py Normal file
View File

@ -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()