Сетевое программирование на Python: протоколы от TCP до HTTP

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Начинающие программисты, интересующиеся сетевым программированием на Python
  • Студенты и разработчики, желающие улучшить свои навыки в создании веб-приложений
  • Профессионалы, которые хотят освоить асинхронное программирование и работу с сетевыми протоколами

    Мир сетевого программирования на Python — это захватывающая территория возможностей, где каждая строка кода может связать устройства по всему миру. Овладение сетевыми протоколами в Python открывает двери к созданию всего: от простых чат-приложений до сложных API и высоконагруженных серверов. Независимо от того, пишете ли вы первый TCP-сервер или оптимизируете существующую систему с асинхронными запросами — понимание основ сетевого взаимодействия критически важно. Погрузимся в мир пакетов, сокетов и протоколов, вооружившись только Python и жаждой знаний! 🚀

Хотите быстро освоить сетевое программирование на Python и создавать профессиональные веб-приложения? Курс Обучение Python-разработке от Skypro превратит вас из новичка в уверенного разработчика. За 9 месяцев вы пройдете путь от основ до реальных проектов с сетевыми протоколами, освоите асинхронное программирование и фреймворки, а менторы помогут преодолеть все сложности. Бонус — портфолио из 5+ проектов и гарантированное трудоустройство!

Основы сетевых протоколов в Python для начинающих

Прежде чем погружаться в код, необходимо понять фундаментальные принципы работы сетевых протоколов. Сетевые протоколы — это стандартизированные правила, определяющие формат и порядок обмена данными между устройствами в сети. Python предлагает мощный инструментарий для работы с различными протоколами.

Сетевая модель OSI и стек TCP/IP формируют основу для понимания сетевого взаимодействия:

  • Физический уровень — передача битов через физическую среду
  • Канальный уровень — MAC-адресация и передача кадров
  • Сетевой уровень — IP-маршрутизация и адресация
  • Транспортный уровень — TCP/UDP протоколы, порты
  • Сеансовый уровень — управление сеансами связи
  • Представительский уровень — форматирование и шифрование данных
  • Прикладной уровень — интерфейс с приложениями (HTTP, FTP, SMTP)

Python предоставляет библиотеки для работы преимущественно с верхними уровнями этой модели. Наиболее важные протоколы, с которыми вы будете работать:

Протокол Уровень Библиотеки Python Особенности
TCP Транспортный socket Надежная, упорядоченная доставка с установлением соединения
UDP Транспортный socket Быстрая доставка без гарантий, без установления соединения
HTTP Прикладной requests, urllib, aiohttp Запрос-ответ, веб-коммуникации
WebSocket Прикладной websockets Двунаправленная связь в реальном времени
MQTT Прикладной paho-mqtt Легковесный протокол для IoT устройств

Александр Петров, руководитель отдела разработки

Когда я только начинал заниматься сетевым программированием, я потратил недели на отладку простого клиент-серверного приложения. Проблема оказалась в непонимании базовых принципов блокирующих операций и буферизации. Клиент отправлял данные, но сервер не всегда их получал целиком.

После изучения основ TCP и особенностей socket-программирования, я понял, что нужно обязательно проверять, сколько данных фактически было отправлено и получено. Добавив простой цикл для приема всех данных до определенного разделителя, я решил проблему за 15 минут.

Этот опыт научил меня, что знание низкоуровневых деталей протоколов критически важно — без них даже самый красивый высокоуровневый код будет работать ненадежно.

Для начала работы с сетевыми протоколами в Python необходимо понимать IP-адресацию и порты. IP-адрес идентифицирует устройство в сети, а порт — конкретное приложение на этом устройстве. В Python для представления этой пары используется кортеж (tuple):

Python
Скопировать код
address = ('127.0.0.1', 8000) # Локальный адрес и порт 8000

Основой для низкоуровневой работы с сетью в Python является модуль socket, входящий в стандартную библиотеку. Он позволяет создавать соединения по различным протоколам и является фундаментом для более высокоуровневых библиотек. 🔌

Пошаговый план для смены профессии

Работа с TCP/IP и UDP через библиотеку socket

Библиотека socket в Python — это мощный интерфейс для создания сетевых приложений. Она позволяет работать с TCP и UDP протоколами на низком уровне, давая полный контроль над сетевым взаимодействием.

Начнем с базового примера TCP-сервера, который прослушивает порт и отвечает на запросы клиентов:

Python
Скопировать код
import socket

# Создаем TCP сокет
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Привязываем сокет к адресу и порту
server_socket.bind(('127.0.0.1', 8000))

# Начинаем прослушивание (максимум 5 ожидающих подключений)
server_socket.listen(5)
print("Сервер запущен на 127.0.0.1:8000")

while True:
# Принимаем входящее подключение
client_socket, client_address = server_socket.accept()
print(f"Подключение от {client_address}")

# Получаем данные от клиента
data = client_socket.recv(1024)
if not data:
break

# Отправляем ответ
response = f"Получено: {data.decode()}"
client_socket.send(response.encode())

# Закрываем соединение с клиентом
client_socket.close()

# Закрываем серверный сокет
server_socket.close()

А вот соответствующий TCP-клиент:

Python
Скопировать код
import socket

# Создаем TCP сокет
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Подключаемся к серверу
client_socket.connect(('127.0.0.1', 8000))

# Отправляем сообщение
message = "Привет, сервер!"
client_socket.send(message.encode())

# Получаем ответ
response = client_socket.recv(1024)
print(f"Ответ сервера: {response.decode()}")

# Закрываем сокет
client_socket.close()

Для работы с UDP протоколом код немного отличается, так как UDP не устанавливает соединение:

Python
Скопировать код
# UDP-сервер
import socket

server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(('127.0.0.1', 8000))

while True:
data, client_address = server_socket.recvfrom(1024)
print(f"Сообщение от {client_address}: {data.decode()}")
response = f"UDP-ответ на: {data.decode()}"
server_socket.sendto(response.encode(), client_address)

И UDP-клиент:

Python
Скопировать код
# UDP-клиент
import socket

client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
message = "Привет, UDP-сервер!"
client_socket.sendto(message.encode(), ('127.0.0.1', 8000))
response, _ = client_socket.recvfrom(1024)
print(f"Ответ: {response.decode()}")
client_socket.close()

Важно понимать различия между TCP и UDP протоколами для выбора оптимального решения для вашей задачи:

Характеристика TCP UDP
Установка соединения Требуется (трехстороннее рукопожатие) Не требуется
Гарантия доставки Гарантирована Не гарантирована
Порядок пакетов Сохраняется Может нарушаться
Скорость Ниже из-за накладных расходов Выше
Контроль перегрузки Есть Нет
Типичное применение Веб, email, файловый обмен Стриминг, игры, DNS

Рассмотрим несколько важных аспектов при работе с socket-программированием в Python:

  1. Блокирующие операции: По умолчанию сокеты в Python блокирующие. Операции вроде accept(), recv() будут блокировать выполнение программы до получения результата.
  2. Неблокирующий режим: Можно перевести сокет в неблокирующий режим с помощью socket.setblocking(False).
  3. Таймауты: Установите таймаут для операций с помощью socket.settimeout(seconds).
  4. Буферизация: Не всегда данные передаются одним куском. Часто требуется организовать цикл для чтения всех данных.
  5. Исключения: Обрабатывайте сетевые исключения (ConnectionRefusedError, ConnectionResetError и т.д.).

При работе с сокетами важно корректно закрывать их после использования, чтобы избежать утечек ресурсов. Рекомендуется использовать контекстный менеджер:

Python
Скопировать код
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(('127.0.0.1', 8000))
s.send(b'Hello, world')
data = s.recv(1024)
# Сокет автоматически закроется после выхода из блока with

Для реальных приложений часто требуется обрабатывать несколько клиентов одновременно. Это можно реализовать с помощью многопоточности, мультипроцессинга или асинхронного программирования, о котором мы поговорим позже. 🧵

Создание HTTP-запросов с помощью requests и urllib

HTTP — самый распространенный протокол прикладного уровня, используемый для передачи данных в веб. Python предлагает несколько библиотек для работы с HTTP, наиболее популярными из которых являются requests и встроенная urllib.

Мария Соколова, Python-разработчик

На одном из проектов мы столкнулись с проблемой – наше приложение выполняло сотни HTTP-запросов к внешнему API, и это становилось узким местом всей системы. Запросы выполнялись последовательно, и общее время обработки достигало минут.

Сначала я попробовала оптимизировать код, используя сессии в requests для повторного использования подключений. Это ускорило процесс, но недостаточно. Затем я переписала код с использованием пула потоков:

Python
Скопировать код
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=20) as executor:
results = list(executor.map(fetch_data, urls))

Время выполнения сократилось с 3 минут до 12 секунд! Но настоящий прорыв произошел, когда мы перешли на асинхронные запросы с aiohttp – время упало до 4 секунд.

Этот опыт показал мне, насколько важно выбирать правильный инструмент для сетевого взаимодействия в зависимости от масштаба и требований проекта.

Библиотека requests — это элегантный и простой HTTP-клиент для Python. Она стала де-факто стандартом благодаря своему интуитивно понятному API:

Python
Скопировать код
import requests

# GET-запрос
response = requests.get('https://api.github.com/events')
print(f"Статус: {response.status_code}")
print(f"Тип содержимого: {response.headers['Content-Type']}")
print(f"Содержимое: {response.text[:100]}...") # Печатаем первые 100 символов

# POST-запрос с JSON-данными
data = {'key': 'value', 'another_key': 'another_value'}
response = requests.post('https://httpbin.org/post', json=data)
print(f"Ответ сервера: {response.json()}")

# Загрузка файла
files = {'file': open('example.txt', 'rb')}
response = requests.post('https://httpbin.org/post', files=files)

# Параметры запроса
params = {'q': 'python', 'sort': 'stars'}
response = requests.get('https://api.github.com/search/repositories', params=params)

# Установка заголовков
headers = {'User-Agent': 'Python Requests App', 'Authorization': 'Bearer token123'}
response = requests.get('https://api.example.com/data', headers=headers)

# Работа с куками
cookies = {'session_id': '123456'}
response = requests.get('https://httpbin.org/cookies', cookies=cookies)

Для более продвинутых сценариев, requests предлагает сессии, позволяющие сохранять определенные параметры и куки между запросами:

Python
Скопировать код
with requests.Session() as session:
# Устанавливаем параметры, которые будут использоваться во всех запросах
session.headers.update({'User-Agent': 'Python Requests App'})
session.auth = ('username', 'password')

# Первый запрос – авторизация
session.get('https://example.com/login')

# Последующие запросы будут использовать те же куки и заголовки
response = session.get('https://example.com/dashboard')

Встроенная библиотека urllib более низкоуровневая, но иногда может быть полезна:

Python
Скопировать код
from urllib.request import urlopen, Request
from urllib.parse import urlencode

# Простой GET-запрос
with urlopen('https://www.python.org') as response:
html = response.read()
print(f"Получено {len(html)} байт")

# POST-запрос с данными
data = urlencode({'name': 'John', 'age': 30}).encode()
req = Request('https://httpbin.org/post', data=data, method='POST')
with urlopen(req) as response:
print(response.read().decode())

# Добавление заголовков
headers = {'User-Agent': 'Mozilla/5.0'}
req = Request('https://api.github.com/events', headers=headers)
with urlopen(req) as response:
print(response.read(100)) # Первые 100 байт

Сравнение библиотек requests и urllib:

  • requests — более высокоуровневый, интуитивно понятный API, поддерживает JSON, аутентификацию, сессии
  • urllib — входит в стандартную библиотеку, не требует установки, более низкоуровневый
  • requests — лучшая обработка ошибок, автоматическая декомпрессия, редиректы
  • urllib — может быть сложнее в использовании, но имеет меньше зависимостей

Работая с HTTP-запросами, важно учитывать следующие моменты:

  1. Обработка ошибок: Всегда проверяйте статус-коды и обрабатывайте исключения.
  2. Таймауты: Устанавливайте разумные таймауты для запросов (requests.get(url, timeout=5)).
  3. Повторные попытки: Реализуйте механизм повторных попыток для ненадежных API.
  4. Ограничение скорости: Учитывайте лимиты API и используйте задержки между запросами.
  5. Параллелизм: Для множественных запросов используйте многопоточность или асинхронность.

Для отладки HTTP-запросов полезно включить логирование:

Python
Скопировать код
import requests
import logging

# Настройка логирования
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("urllib3").setLevel(logging.DEBUG)

# Запрос с включенным логированием
response = requests.get('https://api.github.com/events')

В продакшн-коде рекомендуется использовать requests в сочетании с библиотеками для кэширования, повторных попыток и управления сессиями, такими как requests-cache и requests-retry. 🌐

Асинхронное сетевое программирование с asyncio

Асинхронное программирование — мощный подход для работы с сетевыми операциями, особенно когда необходимо обрабатывать множество соединений одновременно. Python 3.4+ предоставляет библиотеку asyncio для написания асинхронного кода с использованием синтаксиса async/await.

Основное преимущество асинхронного подхода в том, что он позволяет эффективно использовать время ожидания ввода-вывода для выполнения других задач, вместо блокирования всего потока. Это особенно ценно для сетевых приложений, где большую часть времени занимает ожидание ответов от удаленных серверов.

Рассмотрим базовый пример асинхронного TCP-сервера с использованием asyncio:

Python
Скопировать код
import asyncio

async def handle_client(reader, writer):
data = await reader.read(100)
message = data.decode()
addr = writer.get_extra_info('peername')

print(f"Получено {message!r} от {addr!r}")

response = f"Обработано: {message!r}"
writer.write(response.encode())
await writer.drain()

writer.close()
await writer.wait_closed()
print(f"Соединение с {addr!r} закрыто")

async def main():
server = await asyncio.start_server(
handle_client, '127.0.0.1', 8888)

addr = server.sockets[0].getsockname()
print(f'Сервер запущен на {addr}')

async with server:
await server.serve_forever()

asyncio.run(main())

А вот асинхронный TCP-клиент:

Python
Скопировать код
import asyncio

async def tcp_client():
reader, writer = await asyncio.open_connection(
'127.0.0.1', 8888)

message = 'Привет, asyncio сервер!'
print(f'Отправка: {message!r}')
writer.write(message.encode())
await writer.drain()

data = await reader.read(100)
print(f'Получено: {data.decode()!r}')

writer.close()
await writer.wait_closed()

asyncio.run(tcp_client())

Для HTTP-запросов в асинхронном стиле обычно используется библиотека aiohttp:

Python
Скопировать код
import asyncio
import aiohttp

async def fetch(session, url):
async with session.get(url) as response:
return await response.text()

async def main():
# Создаем сессию для повторного использования соединений
async with aiohttp.ClientSession() as session:
# Список URL для запросов
urls = [
'https://api.github.com/events',
'https://api.github.com/emojis',
'https://api.github.com/meta'
]

# Создаем список задач
tasks = [fetch(session, url) for url in urls]

# Запускаем все задачи конкурентно
results = await asyncio.gather(*tasks)

for url, result in zip(urls, results):
print(f"{url}: получено {len(result)} байт")

# Запускаем асинхронный код
asyncio.run(main())

Преимущества асинхронного подхода для сетевых приложений:

  • Высокая масштабируемость — обработка тысяч соединений в одном потоке
  • Эффективное использование ресурсов — нет избыточных потоков
  • Снижение накладных расходов — меньше переключений контекста
  • Упрощенная модель параллелизма — без блокировок и гонок данных
  • Линейное выполнение кода — легче читать и поддерживать

Важные концепции asyncio, которые нужно понимать:

  1. Корутины (coroutines) — функции, объявленные с ключевым словом async def, которые могут приостанавливать выполнение с await.
  2. Задачи (Tasks) — обертки вокруг корутин, позволяющие отслеживать их выполнение.
  3. Цикл событий (Event Loop) — центральный механизм, координирующий выполнение асинхронных задач.
  4. Футуры (Futures) — объекты, представляющие результат асинхронных операций, которые будут доступны в будущем.

Пример более сложной асинхронной работы с управлением ограничением одновременных подключений:

Python
Скопировать код
import asyncio
import aiohttp
from aiohttp import ClientSession

async def fetch_with_semaphore(semaphore, session, url):
# Семафор ограничивает количество одновременных запросов
async with semaphore:
try:
async with session.get(url, timeout=10) as response:
return await response.text(), url
except Exception as e:
return f"Ошибка: {str(e)}", url

async def main():
# Максимум 5 одновременных запросов
semaphore = asyncio.Semaphore(5)

# Список URL
urls = [f"https://httpbin.org/delay/{i%3}" for i in range(20)]

async with ClientSession() as session:
tasks = [
fetch_with_semaphore(semaphore, session, url) 
for url in urls
]

# Обрабатываем результаты по мере их готовности
for future in asyncio.as_completed(tasks):
result, url = await future
print(f"Завершен запрос к {url}: {len(result) if not isinstance(result, str) or not result.startswith('Ошибка') else result}")

asyncio.run(main())

Асинхронное сетевое программирование особенно эффективно для приложений с высокой нагрузкой, таких как API-серверы, прокси, агрегаторы данных и т.д. Однако стоит помнить, что асинхронный код сложнее отлаживать и требует особого внимания к обработке исключений. 🔄

Практика: разработка клиент-серверного приложения

Давайте объединим полученные знания и создадим простое, но полнофункциональное клиент-серверное приложение — асинхронный чат-сервер с поддержкой множества клиентов. Такой проект демонстрирует многие аспекты сетевого программирования в Python.

Начнем с асинхронного сервера, который будет обрабатывать подключения клиентов и рассылать сообщения всем участникам чата:

Python
Скопировать код
# chat_server.py
import asyncio
import json
from datetime import datetime

# Список для хранения подключенных клиентов
connected_clients = []

async def handle_client(reader, writer):
# Получаем информацию о клиенте
client_addr = writer.get_extra_info('peername')
client_id = f"{client_addr[0]}:{client_addr[1]}"
print(f"Новое подключение от {client_id}")

# Добавляем клиента в список
connected_clients.append(writer)

# Отправляем приветственное сообщение
welcome_message = {
"type": "system",
"time": datetime.now().strftime("%H:%M:%S"),
"content": f"Добро пожаловать в чат! Онлайн пользователей: {len(connected_clients)}"
}
writer.write(json.dumps(welcome_message).encode() + b'\n')
await writer.drain()

# Оповещаем всех о новом пользователе
broadcast_message = {
"type": "system",
"time": datetime.now().strftime("%H:%M:%S"),
"content": f"Пользователь {client_id} присоединился к чату"
}
await broadcast(broadcast_message, writer)

try:
while True:
# Получаем данные от клиента
data = await reader.readline()
if not data: # Соединение закрыто
break

message = data.decode().strip()
print(f"Получено от {client_id}: {message}")

try:
# Пытаемся разобрать JSON
parsed_message = json.loads(message)
parsed_message["time"] = datetime.now().strftime("%H:%M:%S")
parsed_message["sender"] = client_id

# Рассылаем сообщение всем клиентам
await broadcast(parsed_message, None)
except json.JSONDecodeError:
error_message = {
"type": "error",
"time": datetime.now().strftime("%H:%M:%S"),
"content": "Неверный формат сообщения"
}
writer.write(json.dumps(error_message).encode() + b'\n')
await writer.drain()

except asyncio.CancelledError:
pass
except Exception as e:
print(f"Ошибка при обработке клиента {client_id}: {str(e)}")
finally:
# Удаляем клиента из списка при отключении
if writer in connected_clients:
connected_clients.remove(writer)

# Закрываем соединение
writer.close()
await writer.wait_closed()

# Оповещаем о выходе пользователя
leave_message = {
"type": "system",
"time": datetime.now().strftime("%H:%M:%S"),
"content": f"Пользователь {client_id} покинул чат"
}
await broadcast(leave_message, None)
print(f"Соединение с {client_id} закрыто")

async def broadcast(message, exclude):
"""Отправляет сообщение всем подключенным клиентам, кроме отправителя"""
encoded_message = json.dumps(message).encode() + b'\n'

for client in connected_clients:
if client != exclude:
try:
client.write(encoded_message)
await client.drain()
except:
# Если отправка не удалась, просто пропускаем клиента
pass

async def main():
server = await asyncio.start_server(
handle_client, '0.0.0.0', 8888)

addr = server.sockets[0].getsockname()
print(f'Чат-сервер запущен на {addr}')

async with server:
await server.serve_forever()

if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("Сервер остановлен")

Теперь создадим клиентское приложение, которое будет подключаться к серверу и обмениваться сообщениями:

Python
Скопировать код
# chat_client.py
import asyncio
import json
import sys
import aioconsole

async def receive_messages(reader):
"""Асинхронно получает и выводит сообщения от сервера"""
try:
while True:
data = await reader.readline()
if not data:
print("Соединение с сервером потеряно")
break

message = json.loads(data.decode())

if message["type"] == "system":
print(f"[{message['time']}] СИСТЕМА: {message['content']}")
elif message["type"] == "message":
print(f"[{message['time']}] {message['sender']}: {message['content']}")
elif message["type"] == "error":
print(f"[{message['time']}] ОШИБКА: {message['content']}")
except Exception as e:
print(f"Ошибка при получении сообщений: {str(e)}")
finally:
# Завершаем программу при потере соединения
sys.exit(0)

async def send_messages(writer):
"""Асинхронно отправляет сообщения на сервер"""
try:
print("Введите ваше имя:")
username = await aioconsole.ainput()

print("Введите сообщение (или /exit для выхода):")

while True:
# Асинхронно ждем ввода пользователя
message_text = await aioconsole.ainput()

if message_text.lower() == '/exit':
break

# Формируем JSON-сообщение
message = {
"type": "message",
"username": username,
"content": message_text
}

# Отправляем на сервер
writer.write(json.dumps(message).encode() + b'\n')
await writer.drain()
except Exception as e:
print(f"Ошибка при отправке сообщений: {str(e)}")
finally:
# Закрываем соединение при выходе
writer.close()
await writer.wait_closed()
sys.exit(0)

async def main():
try:
# Подключаемся к серверу
reader, writer = await asyncio.open_connection('127.0.0.1', 8888)
print("Подключено к серверу чата")

# Запускаем две задачи: отправка и прием сообщений
receive_task = asyncio.create_task(receive_messages(reader))
send_task = asyncio.create_task(send_messages(writer))

# Ждем завершения любой из задач
await asyncio.gather(receive_task, send_task, return_exceptions=True)
except ConnectionRefusedError:
print("Не удалось подключиться к серверу. Убедитесь, что сервер запущен.")
except Exception as e:
print(f"Ошибка: {str(e)}")

if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("Клиент остановлен")

Этот пример демонстрирует несколько важных концепций сетевого программирования:

  1. Асинхронная обработка соединений — сервер может обрабатывать множество клиентов одновременно
  2. Двусторонняя коммуникация — клиенты и сервер обмениваются сообщениями
  3. Протокол приложения — использование JSON для структурированных сообщений
  4. Обработка ошибок и исключений — корректное закрытие соединений
  5. Управление состоянием — отслеживание подключенных клиентов

Возможные улучшения этого приложения:

  • Добавление аутентификации пользователей
  • Сохранение истории сообщений в базе данных
  • Шифрование сообщений для конфиденциальности
  • Добавление приватных сообщений между пользователями
  • Создание графического интерфейса для клиента
  • Внедрение механизма сохранения сообщений при отсутствии подключения

Данный пример можно расширить до полноценного мессенджера или использовать как основу для других сетевых приложений. Ключевой момент — правильное проектирование протокола обмена данными между клиентом и сервером. 📱

Python — идеальный инструмент для работы с сетевыми протоколами благодаря богатству библиотек и элегантному синтаксису. Изучив представленные в руководстве подходы — от низкоуровневых сокетов до высокоуровневых HTTP-клиентов и асинхронного программирования — вы получаете мощный арсенал для создания любых сетевых приложений. Главное помнить: каждая задача требует правильного инструмента. Используйте библиотеку socket для полного контроля над соединениями, requests для простых HTTP-запросов и asyncio для масштабируемых решений. Экспериментируйте, тестируйте под нагрузкой и не забывайте об обработке ошибок — это фундамент надежного сетевого кода.

Загрузка...