7.1 Что такое сокеты?
Давайте копнем еще глубже. Сначала мы научились работать с request, затем с http.client, затем с прокси. Что же дальше? А дальше мы заглянем всем этим библиотекам под капот…
Сокет (дословно — розетка) — это точка в сети, через которую данные отправляются и принимаются. Сокет можно представить как конечную точку двустороннего канала связи между двумя программами, работающими на одной или разных машинах.
Сокеты поддерживают различные сетевые протоколы, но наиболее часто используются два:
-
TCP(Transmission Control Protocol): Надёжный протокол, который обеспечивает установление соединения, проверку целостности данных и их правильную последовательность. -
UDP(User Datagram Protocol): Протокол, не ориентированный на соединение, который не гарантирует доставку данных, но является более быстрым и эффективным для определённых типов приложений.
Для идентификации сокета используются IP-адрес (идентифицирующий устройство в сети) и порт (идентифицирующий конкретное приложение или службу на устройстве).
Важно! IP-адрес и порт однозначно идентифицируют программу в сети. Это как адрес дома и номер квартиры. Адрес дома (IP-адрес) — это адрес вашего компьютера в сети, а порт — это номер квартиры, который программа использует для получения и отправления сообщений.
Более подробно о том, что такое IP-адрес и порт, вы можете узнать в лекциях, посвящённых устройству сети.
Основные этапы работы программы с сокетами:
- Создание сокета: Программа создаёт сокет, указывая тип протокола (
TCPилиUDP). - Связывание с адресом: Сокет связывается с IP-адресом и номером порта, чтобы быть доступным для соединений или отправки/получения данных.
- Прослушивание и установление соединения (для
TCP):- Прослушивание: Сокет на стороне сервера переводится в режим прослушивания, ожидая входящих соединений.
- Установление соединения: Клиент инициирует соединение с сервером. Сервер принимает соединение, создавая новый сокет для взаимодействия с клиентом.
- Обмен данными: Данные передаются между клиентом и сервером. В случае
TCPданные передаются в надёжном порядке. - Закрытие соединения: После завершения обмена данными соединение закрывается.
Преимущества использования сокетов:
- Гибкость: Сокеты позволяют приложениям обмениваться данными независимо от их местоположения и платформы.
- Производительность: Сокеты обеспечивают быстрый и эффективный способ передачи данных, особенно в случае использования
UDP. - Надёжность (в случае
TCP): ПротоколTCPобеспечивает надёжную доставку данных с проверкой целостности и восстановлением утраченных пакетов.
7.2 Создание сокет-сервера
Работа с сокетами в Python осуществляется с помощью встроенного модуля socket, который предоставляет интерфейс для низкоуровневого сетевого программирования.
С помощью сокетов можно создать socket-сервер — приложение/объект, который будет получать запросы от клиентов и отвечать им. А также socket-клиент — приложение/объект, который будет отправлять запросы socket-серверу и получать от него ответы.
Чтобы создать socket-сервер, нужно выполнить три действия:
- Создать объект
socket-сервера. - Привязать (
bind) его к какому-нибудь IP и порту. - Включить режим прослушивания (
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. Эти параметры наиболее часто используются для создания сетевых приложений.
После того как поступило входящее сообщение, нужно сделать ещё четыре действия:
- Установить
(accept)соединение с клиентом. - Получить
(receive)запрос (данные) от клиента. - Отправить
(send)клиенту ответ — тоже какие-то данные. - Закрыть
(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 Создание сокет-клиента
Сокет-сервер мы создали, теперь давайте напишем свой сокет-клиент, который будет обращаться к серверу и получать от него данные в ответ.
Для этого нужно выполнить пять действий:
- Создать объект
socket-клиента. - Установить соединение
(connect)с IP-адресом и портом нашегоsocket-сервера. - Отправить
(send)сообщение на сервер. - Получить
(receive)данные от сервера. - Закрыть
(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-адреса, установка соединений, прослушивание запросов и все в том же роде. Так что понимание того, как это работает глубоко внутри, очень сильно облегчит вам жизнь и поможет объединить разрозненные знания в единую картину.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ