Модуль socket

Модуль 1: Python Core
13 уровень , 6 лекция
Открыта

7.1 Что такое сокеты?

Давайте копнем еще глубже. Сначала мы научились работать с request, затем с http.client, затем с прокси. Что же дальше? А дальше мы заглянем всем этим библиотекам под капот…

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

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

  • TCP (Transmission Control Protocol): Надёжный протокол, который обеспечивает установление соединения, проверку целостности данных и их правильную последовательность.
  • UDP (User Datagram Protocol): Протокол, не ориентированный на соединение, который не гарантирует доставку данных, но является более быстрым и эффективным для определённых типов приложений.

Для идентификации сокета используются IP-адрес (идентифицирующий устройство в сети) и порт (идентифицирующий конкретное приложение или службу на устройстве).

Важно! IP-адрес и порт однозначно идентифицируют программу в сети. Это как адрес дома и номер квартиры. Адрес дома (IP-адрес) — это адрес вашего компьютера в сети, а порт — это номер квартиры, который программа использует для получения и отправления сообщений.

Более подробно о том, что такое IP-адрес и порт, вы можете узнать в лекциях, посвящённых устройству сети.

Основные этапы работы программы с сокетами:

  1. Создание сокета: Программа создаёт сокет, указывая тип протокола (TCP или UDP).
  2. Связывание с адресом: Сокет связывается с IP-адресом и номером порта, чтобы быть доступным для соединений или отправки/получения данных.
  3. Прослушивание и установление соединения (для TCP):
    • Прослушивание: Сокет на стороне сервера переводится в режим прослушивания, ожидая входящих соединений.
    • Установление соединения: Клиент инициирует соединение с сервером. Сервер принимает соединение, создавая новый сокет для взаимодействия с клиентом.
  4. Обмен данными: Данные передаются между клиентом и сервером. В случае TCP данные передаются в надёжном порядке.
  5. Закрытие соединения: После завершения обмена данными соединение закрывается.

Преимущества использования сокетов:

  • Гибкость: Сокеты позволяют приложениям обмениваться данными независимо от их местоположения и платформы.
  • Производительность: Сокеты обеспечивают быстрый и эффективный способ передачи данных, особенно в случае использования UDP.
  • Надёжность (в случае TCP): Протокол TCP обеспечивает надёжную доставку данных с проверкой целостности и восстановлением утраченных пакетов.

7.2 Создание сокет-сервера

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

С помощью сокетов можно создать socket-сервер — приложение/объект, который будет получать запросы от клиентов и отвечать им. А также socket-клиент — приложение/объект, который будет отправлять запросы socket-серверу и получать от него ответы.

Чтобы создать socket-сервер, нужно выполнить три действия:

  1. Создать объект socket-сервера.
  2. Привязать (bind) его к какому-нибудь IP и порту.
  3. Включить режим прослушивания (listen) входящих сообщений.

Выглядеть этот код будет примерно так:


import socket

# Создание сокета
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
# Связывание сокета с адресом и портом
server_socket.bind(('localhost', 12345))
    
# Прослушивание входящих соединений
server_socket.listen(5)
print("Сервер ожидает соединения...")

Здесь socket.AF_INET указывает, что мы используем IPv4 для сетевого протокола, а socket.SOCK_STREAM означает, что мы используем TCP. Эти параметры наиболее часто используются для создания сетевых приложений.

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

  1. Установить (accept) соединение с клиентом.
  2. Получить (receive) запрос (данные) от клиента.
  3. Отправить (send) клиенту ответ — тоже какие-то данные.
  4. Закрыть (close) соединение.

Выглядит этот код примерно так:


# Принятие нового соединения
client_socket, client_address = server_socket.accept()
print(f"Соединение установлено с {client_address}")

# Получение данных от клиента
data = client_socket.recv(1024)
print(f"Получено: {data.decode('utf-8')}")

# Отправка данных клиенту
client_socket.sendall(b'Hello, client!')

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

Метод sendall() используется вместо send(), потому что он гарантирует, что все данные будут отправлены. Метод send() может отправить только часть данных, если буфер заполнится.

Число 1024 в recv(1024) указывает максимальное количество байт, которое можно получить за один раз. Это помогает контролировать объем данных, обрабатываемых за одну операцию.

Код последнего примера обычно выполняется в бесконечном цикле — сервер обрабатывает запрос, затем ждёт нового, затем его обрабатывает, и так постоянно.

Если вы хотите запустить его у себя или просто увидеть полный пример, то приведу его здесь:


import socket

# Создание сокета
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        
# Связывание сокета с адресом и портом
server_socket.bind(('localhost', 12345))
        
# Прослушивание входящих соединений
server_socket.listen(5)
print("Сервер ожидает соединения...")
        
while True:
    # Принятие нового соединения
    client_socket, client_address = server_socket.accept()
    print(f"Соединение установлено с {client_address}")
        
    # Получение данных от клиента
    data = client_socket.recv(1024)
    print(f"Получено: {data.decode('utf-8')}")
        
    # Отправка данных клиенту
    client_socket.sendall(b'Hello, client!')
        
    # Закрытие соединения с клиентом
    client_socket.close()

7.3 Создание сокет-клиента

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

Для этого нужно выполнить пять действий:

  1. Создать объект socket-клиента.
  2. Установить соединение (connect) с IP-адресом и портом нашего socket-сервера.
  3. Отправить (send) сообщение на сервер.
  4. Получить (receive) данные от сервера.
  5. Закрыть (close) соединение.

На самом деле это проще, чем кажется. Вот как будет выглядеть этот код:


import socket

# Создание сокета
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
# Установка соединения с сервером
client_socket.connect(('localhost', 12345))
    
# Отправка данных на сервер
client_socket.sendall(b'Hello, server!')
    
# Получение данных от сервера
data = client_socket.recv(1024)
print(f"Получено от сервера: {data.decode('utf-8')}")
    
# Закрытие сокета
client_socket.close()

Если на той стороне нет запущенного socket-сервера или соединение оборвалось, то возникнет исключение типа socket.error. Так что не забывайте обрабатывать исключения.

На этом мы сегодня закончим работу с сокетами.

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

2
Задача
Модуль 1: Python Core, 13 уровень, 6 лекция
Недоступна
Создание сокет-сервера
Создание сокет-сервера
2
Задача
Модуль 1: Python Core, 13 уровень, 6 лекция
Недоступна
Создание сокет-клиента
Создание сокет-клиента
Комментарии (4)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Slevin Уровень 64
11 июля 2025
Валидатор полная параша и не принимает абсолютно рабочий пример из лекции. Причем ничего не объясняя - просто все задачи не выполнены. Нет никакого желания заниматься экспериментами ради получения валидации. Остается только копировать готовое "решение". Это делает процесс практического обучения еще бесполезнее, чем оно было до этого (где было достаточно копировать из примеров из лекций). Это абсолютный и недопустимый мусор.
Slevin Уровень 64
11 июля 2025
в то же время, валидатор второй задачи принял код без проблем и вопросов. То есть косяк именно с валидатором в первой задачи, который проверяет видимо фазу луны или идентичную копию "правильного" ответа
Дмитрий Уровень 27
18 мая 2025
Валидатор игнорирует контент-менеджер, и выдаёт ошибку там, где её нет. Если socket используется с with, то отдельно закрывать соединение не нужно, однако валидатор настаивает на бесполезном и неправильном методе .close. Удивительно, что он ещё .shutdown не требует.
Japan_Dragon Уровень 32
28 февраля 2025
server_socket.listen(5) 5 означает, что если в момент, когда сервер занят обслуживанием одного клиента, приходят еще 5 подключений, они будут помещены в очередь, и сервер обработает их по мере освобождения. Если очередь переполнена, новые подключения могут быть отклонены или в некоторых случаях они могут ожидать нового освобождения слота.