Сокеты: что это, как работают и пример на 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 Журнал
Распродажа ноябрьских курсов (поп-ап)
Бесплатные открытые уроки (поп-ап)