Сообщить об ошибке.

Низкоуровневый сетевой интерфейс в Python

Модуль socket обеспечивает доступ к интерфейсу сокета BSD. Он доступен во всех современных системах Unix, Windows, MacOS и, возможно, на дополнительных платформах.

Он включает в себя функции создания объекта сокета Socket, который и обрабатывает канал данных, а также функции, связанных с сетевыми задачами, такими как преобразование имени сервера в IP адрес и форматирование данных для отправки по сети.

Примечание. Поведение модуля может зависеть от платформы, поскольку выполняются вызовы API сокетов операционной системы.

Интерфейс Python представляет собой прямую трансляцию системного вызова Unix и интерфейса библиотеки для сокетов в объектно-ориентированный стиль Python. Функция socket.socket() возвращает объект Socket, методы которого реализуют различные системные вызовы сокетов.

Типы параметров функций модуля несколько более высокоуровневые, чем в интерфейсе языка C: как и в случае операций чтения/записи с файлами, распределение буфера при операциях приема данных происходит автоматически, а длина буфера неявно определяется операциями отправки.

Сокеты можно настроить для работы в качестве сервера и прослушивания входящих сообщений или для подключения к другим приложениям в качестве клиента. После подключения обоих концов сокета TCP/IP обмен данными становится двунаправленным.

Пример создания и использования сокетов на примере TCP/IP сервер и клиента.

Этот пример, основанный на стандартной документации библиотеки, принимает входящие сообщения и передает их обратно отправителю. Он начинается с создания сокета TCP/IP, а затем метод sock.bind() используется для связывания сокета с адресом сервера.

Примечание. Для успешного тестирования примера, код клиента и сервера необходимо запускать в разных окнах терминала. Код сервера запускается первым.

TCP/IP сервер.

# 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() для подключения сокета непосредственно к удаленному адресу.

TCP/IP клиент.

# 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!
Закрываем сокет