Сокеты: что это, как работают и пример на Python

Сокеты: что это, как работают и пример на Python Полезное

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

Ниже разберем, из чего состоит сокет и на каком уровне он работает, чем отличаются сокеты TCP и UDP, как устроена клиент-серверная модель и какие шаги проходит соединение. В конце — рабочий пример клиент-сервера на Python с фактическим выводом и разбор частых ошибок.

Что такое сокет и как он работает

Сокет однозначно определяется парой значений: IP-адрес и номер порта. IP-адрес указывает на конкретное устройство в сети, а порт — на конкретную программу или службу на этом устройстве. Например, веб-сервер обычно слушает порт 443 (HTTPS), а почтовый сервер — порт 25.

Полное соединение описывается уже четверкой: IP и порт клиента плюс IP и порт сервера. Именно поэтому к одному порту сервера могут быть одновременно подключены сотни клиентов — их различают по адресу и порту на стороне клиента.

Сам сокет создает и обслуживает операционная система. Для программы он выглядит как объект (в Unix — как файловый дескриптор), в который можно писать данные и из которого можно читать. Реальную доставку байтов по сети берет на себя сетевой стек ОС, а приложение работает с удобным интерфейсом.

Виды сокетов: TCP и UDP

Чаще всего под сокетами понимают два типа — потоковые (TCP) и датаграммные (UDP). Выбор между ними определяется тем, что важнее: надежность доставки или скорость и минимальные задержки.

Признак TCP (потоковый, SOCK_STREAM) UDP (датаграммный, SOCK_DGRAM)
Соединение С установкой соединения Без установки соединения
Гарантия доставки Есть, с переотправкой потерь Нет, пакет может потеряться
Порядок данных Сохраняется Не гарантируется
Скорость и накладные расходы Медленнее, накладных расходов больше Быстрее, минимум накладных расходов
Где применяют Веб, почта, передача файлов, БД Онлайн-игры, видеозвонки, DNS, стриминг

Ориентир для выбора простой. Нужна гарантия, что данные дойдут целиком и по порядку (загрузка страницы, файл, запрос к базе) — берут TCP. Важнее скорость и свежесть данных, а редкая потеря пакета некритична (голос, видео, игровое положение игрока) — берут UDP. В большинстве учебных задач на Python по умолчанию используют TCP.

Клиент-серверная модель и этапы соединения

В клиент-серверной модели одна сторона (сервер) ждет подключений, другая (клиент) их инициирует. Роли задаются набором вызовов сокета.

На стороне сервера порядок такой:

  • socket() — создать сокет;
  • bind() — привязать сокет к IP и порту;
  • listen() — перевести сокет в режим ожидания входящих подключений;
  • accept() — принять подключение и получить отдельный сокет для общения с этим клиентом.

На стороне клиента шагов меньше: socket() создает сокет, а connect() подключается к адресу и порту сервера. Дальше обе стороны обмениваются данными через send() и recv() и закрывают соединение через close().

Для TCP перед обменом данными происходит «трехэтапное рукопожатие» (three-way handshake): клиент шлет пакет SYN, сервер отвечает SYN-ACK, клиент подтверждает пакетом ACK. Только после этого канал готов. У UDP такого шага нет — данные отправляют сразу.

Сокеты в Python: пример клиент-сервер

В стандартной библиотеке Python за работу с сокетами отвечает встроенный модуль socket — ставить ничего не нужно. Примеры ниже проверены на Python 3.12. Задача: клиент запрашивает у сервера текущие дату и время.

Сначала сервер. Запускать его нужно первым, в отдельном терминале.

import socket
from datetime import datetime

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("localhost", 7777))   # привязка к адресу и порту
server.listen(5)                    # 5 - размер очереди ожидающих подключений
print("Сервер слушает порт 7777, жду клиента...")

conn, addr = server.accept()        # блокируется, пока не подключится клиент
print("Клиент подключился:", addr)

now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
conn.sendall(now.encode("utf-8"))   # строку перед отправкой кодируем в байты
conn.close()
server.close()

Вывод в терминале сервера после подключения клиента:

Сервер слушает порт 7777, жду клиента...
Клиент подключился: ('127.0.0.1', 54134)

Теперь клиент — запускаем во втором терминале:

import socket

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("localhost", 7777))   # адрес и порт сервера
data = client.recv(1024)               # принять до 1024 байт
print("Время от сервера:", data.decode("utf-8"))   # байты обратно в строку
client.close()

Вывод в терминале клиента:

Время от сервера: 2026-09-09 14:22:05

Два момента, которые важно понять на этом примере. Число в listen(5) — это длина очереди еще не принятых подключений, а не число «одновременных клиентов»: чтобы обслуживать многих клиентов сразу, accept() вызывают в цикле и обрабатывают каждого в отдельном потоке или через асинхронность. Размер буфера в recv(1024) задает верхнюю границу порции за одно чтение: слишком маленький буфер заставит читать много раз, слишком большой зря займет память, поэтому обычно берут степень двойки от 1024 до 8192 байт.

Частые ошибки

Метод bind, а не blind. Опечатка встречается часто, потому что слова похожи.

server.blind(("localhost", 7777))

Результат:

AttributeError: 'socket' object has no attribute 'blind'

Исправление — правильное имя метода:

server.bind(("localhost", 7777))

Через сокет передают байты, а не строку. Если отправить обычную строку, Python выдаст ошибку типа.

client.send("привет")

Результат:

TypeError: a bytes-like object is required, not 'str'

Исправление — закодировать строку в байты перед отправкой:

client.send("привет".encode("utf-8"))

Клиент стартовал раньше сервера. Если сервер еще не слушает порт, connect() завершится ошибкой.

ConnectionRefusedError: [Errno 61] Connection refused

Исправление — сначала запустить сервер (он должен дойти до listen() и accept()), затем клиента; при разработке подключение можно оборачивать в повтор с паузой.

Выводы

  • Сокет — это конечная точка соединения, однозначно заданная парой IP-адрес и порт; создает и обслуживает его операционная система, а программа работает с ним как с объектом для чтения и записи.
  • Полное TCP-соединение определяется четверкой адрес-порт клиента и адрес-порт сервера, поэтому один порт сервера обслуживает множество клиентов одновременно.
  • TCP дает надежную упорядоченную доставку и подходит для веба, файлов и баз данных; UDP быстрее и без гарантий — для голоса, видео и игр.
  • Роли в клиент-серверной модели задаются вызовами: сервер идет по цепочке socket -> bind -> listen -> accept, клиент — socket -> connect.
  • В Python сокеты доступны сразу через встроенный модуль socket; данные передаются в виде байтов, поэтому строки кодируют через encode и декодируют через decode.

Где применяется / связь с практикой

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

Освойте тему на практике

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

Смежные темы: Клиент-серверная модель в Питоне и сокеты, Логирование в Python.

FAQ

Сокет и порт — это одно и то же?
Нет. Порт — это только число, которое указывает на программу на устройстве. Сокет — это конечная точка соединения целиком, то есть пара из IP-адреса и порта плюс объект в операционной системе, через который идет обмен данными.

Сколько клиентов может обслуживать один порт сервера?
Технически много: соединения различаются по четверке адрес-порт клиента и сервера, а не по одному порту. Реальный предел задают ресурсы машины и способ обработки — потоки, процессы или асинхронность.

Нужны ли сторонние библиотеки для сокетов в Python?
Нет, базовой работы хватает встроенного модуля socket. Сторонние инструменты (например, asyncio для асинхронности или фреймворки) берут, когда нужно обслуживать много соединений одновременно или строить высокоуровневый протокол.

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)