Add lab3 network examples
This commit is contained in:
commit
e66d4fa927
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.venv/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
.env
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
|
||||||
130
README.md
Normal file
130
README.md
Normal file
@ -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`.
|
||||||
42
gitea_api.py
Normal file
42
gitea_api.py
Normal 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
8
http_requests_client.py
Normal 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
33
http_socket_client.py
Normal 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
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
requests
|
||||||
|
|
||||||
21
tcp_client.py
Normal file
21
tcp_client.py
Normal 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
39
tcp_server.py
Normal 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
20
udp_client.py
Normal 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
31
udp_server.py
Normal 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()
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user