Модуль socket
обеспечивает доступ к интерфейсу сокета BSD. Он доступен во всех современных системах Unix, Windows, MacOS и, возможно, на дополнительных платформах.
Он включает в себя функции создания объекта сокета Socket
, который и обрабатывает канал данных, а также функции, связанных с сетевыми задачами, такими как преобразование имени сервера в IP адрес и форматирование данных для отправки по сети.
Примечание. Поведение модуля может зависеть от платформы, поскольку выполняются вызовы API сокетов операционной системы.
Интерфейс Python представляет собой прямую трансляцию системного вызова Unix и интерфейса библиотеки для сокетов в объектно-ориентированный стиль Python. Функция socket.socket()
возвращает объект Socket
, методы которого реализуют различные системные вызовы сокетов.
Типы параметров функций модуля несколько более высокоуровневые, чем в интерфейсе языка C: как и в случае операций чтения/записи с файлами, распределение буфера при операциях приема данных происходит автоматически, а длина буфера неявно определяется операциями отправки.
Сокеты можно настроить для работы в качестве сервера и прослушивания входящих сообщений или для подключения к другим приложениям в качестве клиента. После подключения обоих концов сокета TCP/IP обмен данными становится двунаправленным.
Этот пример, основанный на стандартной документации библиотеки, принимает входящие сообщения и передает их обратно отправителю. Он начинается с создания сокета TCP/IP, а затем метод sock.bind()
используется для связывания сокета с адресом сервера.
Примечание. Для успешного тестирования примера, код клиента и сервера необходимо запускать в разных окнах терминала. Код сервера запускается первым.
# test-server.py import socket import sys # создаемTCP/IP сокет sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Привязываем сокет к порту server_address = ('localhost', 10000) print('Старт сервера на {} порт {}'.format(*server_address)) sock.bind(server_address) # Слушаем входящие подключения sock.listen(1) while True: # ждем соединения print('Ожидание соединения...') connection, client_address = sock.accept() try: print('Подключено к:', client_address) # Принимаем данные порциями и ретранслируем их while True: data = connection.recv(16) print(f'Получено: {data.decode()}') if data: print('Обработка данных...') data = data.upper() print('Отправка обратно клиенту.') connection.sendall(data) else: print('Нет данных от:', client_address) break finally: # Очищаем соединение connection.close()
Вызов метода sock.listen(1)
переводит сокет в режим сервера, а метод sock.accept()
ожидает входящего соединения. Целочисленный аргумент у метода .listen
- это количество соединений, которые система должна поставить в очередь в фоновом режиме, прежде чем отклонять новых клиентов. В этом примере предполагается, что одновременно будет работать только одно соединение.
Метод sock.accept()
возвращает открытое соединение между сервером и клиентом вместе с адресом клиента. На самом деле соединение представляет собой другой сокет на другом порту (назначенный ядром). Данные считываются из соединения с помощью метод sock.recv()
и передаются с помощью sock.sendall()
.
Когда общение с клиентом завершено, соединение необходимо очистить с помощью sock.close()
. В этом примере используется блок try/finally
, чтобы гарантировать, что метод sock.close()
всегда вызывается, даже в случае ошибки.
Клиентская программа настраивает свой сокет иначе, чем сервер. Вместо привязки к порту и прослушивания он использует метод sock.connect()
для подключения сокета непосредственно к удаленному адресу.
# test-client.py import socket import sys # СоздаемTCP/IP сокет sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Подключаем сокет к порту, через который прослушивается сервер server_address = ('localhost', 10000) print('Подключено к {} порт {}'.format(*server_address)) sock.connect(server_address) try: # Отправка данных mess = 'Hello Wоrld!' print(f'Отправка: {mess}') message = mess.encode() sock.sendall(message) # Смотрим ответ amount_received = 0 amount_expected = len(message) while amount_received < amount_expected: data = sock.recv(16) amount_received += len(data) mess = data.decode() print(f'Получено: {data.decode()}') finally: print('Закрываем сокет') sock.close()
После установления соединения данные могут быть отправлены через сокет с помощью метода sock.sendall()
и получены с помощью sock.recv()
, как и на сервере. Когда все сообщения отправлены, а копия получена, то сокет закрывается, чтобы освободить порт.
Клиент и сервер должны запускаться в отдельных окнах терминала, чтобы они могли взаимодействовать друг с другом. Выходные данные сервера показывают входящее соединение и данные, а также ответ, отправленный обратно клиенту.
Старт сервера на localhost порт 10000 Ожидание соединения... Подключено к: ('127.0.0.1', 34800) Получено: Hello Wоrld! Обработка данных... Отправка обратно клиенту. Получено: Нет данных от: ('127.0.0.1', 34800) Ожидание соединения... ...
Выходные данные клиента показывают исходящее сообщение и ответ сервера.
Подключено к localhost порт 10000 Отправка: Hello Wоrld! Получено: HELLO WоRLD! Закрываем сокет