Перейти к основному содержимому

Сетевое программирование на Python

Разработчику Архитектору

См. также: Работа с файлами, сетью и внешними API · Асинхронность и многопоточность · раздел «Сети»

Зачем учить сокеты, если есть requests и FastAPI

В повседневной работе Python-разработчик чаще пишет requests.get(...) или поднимает API на FastAPI — см. главу про файлы, сеть и API.

Под капотом и requests, и браузер, и Uvicorn используют сокеты — интерфейс ОС для обмена байтами по сети.

Понимание сокетов помогает:

  • разбираться в «connection refused», timeout, «broken pipe»;
  • писать свои протоколы (чат, игра, IoT);
  • читать сетевую документацию без ощущения «магии библиотеки».

Модуль socket — в стандартной библиотеке Python.


Слои — от приложения до провода

Ваш код (HTTP, JSON, свой протокол)

HTTP / WebSocket / TLS

TCP или UDP

IP (127.0.0.1, 192.168…)

Сетевой интерфейс

HTTP — правила текста поверх TCP. Сокет — уровень ниже: «отправь эти байты на порт».


Словарь

ТерминПростыми словами
IP-адрес«Номер» машины в сети, например 127.0.0.1 (localhost)
ПортНомер службы на машине: 80/443 — веб, учебник — 50007
TCPНадёжный поток байтов
UDPПакеты без гарантии доставки
КлиентВызывает connect
Серверbind + listen + accept
СокетКанал для send / recv

Модель клиент–сервер

Сокеты работают с байтами (b"текст"), не со строками. Кодировку (UTF-8) выбирает ваш код.


TCP — эхо-сервер — разбор построчно

import socket

HOST = "127.0.0.1"
PORT = 50007

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((HOST, PORT))
server.listen(1)
conn, addr = server.accept()
with conn:
print("Подключение:", addr)
while True:
data = conn.recv(1024)
if not data:
break
conn.sendall(data)
СтрокаСмысл
AF_INETIPv4
SOCK_STREAMTCP
SO_REUSEADDRБыстрее перезапуск после остановки
bind / listenЗанять порт и ждать
accept()Ждать клиента
recv(1024)До 1024 байт за вызов
if not dataКлиент закрыл соединение
sendallОтправить все байты

Клиент:

import socket

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client:
client.connect(("127.0.0.1", 50007))
client.sendall(b"ping")
print(client.recv(1024))

Длинные сообщения читают в цикле или по заголовку с длиной.


UDP

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(b"hello", ("127.0.0.1", 50008))
data, addr = sock.recvfrom(1024)
sock.close()

sendto / recvfrom — без долгой сессии, как у TCP.


Несколько клиентов

ПодходКогда
Поток на соединениеМало клиентов, простая логика
asyncioМного I/O
Nginx + UvicornПродакшен HTTP
import threading

def handle(conn):
with conn:
while True:
chunk = conn.recv(4096)
if not chunk:
break
conn.sendall(chunk)

asyncio

import asyncio

async def tcp_echo_client():
reader, writer = await asyncio.open_connection("127.0.0.1", 50007)
writer.write(b"async ping\n")
await writer.drain()
data = await reader.read(100)
print(data.decode())
writer.close()
await writer.wait_closed()

asyncio.run(tcp_echo_client())

Связь с HTTP

HTTP-запрос — текст в TCP-потоке. requests открывает сокет, при HTTPS добавляет TLS.

НастройкаУровень
timeoutОжидание байтов на сокете
Keep-AliveОдин TCP на несколько запросов

В проектах HTTP отдают WSGI/ASGI — веб и API.


Ошибки и безопасность

СитуацияПоведение
Порт занятAddress already in use
Сервер выключенConnection refused
Разрывrecvb""
ТаймаутsettimeoutTimeoutError

На байтах из сети не вызывайте eval / pickle от незнакомых клиентов. Лучше JSON + длина сообщения.


Когда что выбирать

ЗадачаИнструмент
REST, вебrequests, Flask, FastAPI, Django
Двусторонний каналWebSocket
Мелкие пакеты, потеря допустимаUDP
Учебный разбор транспортаsocket, asyncio

Связанные материалы


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).