Рубрики
Без рубрики

Доменные сокеты Unix

Автор оригинала: Doug Hellmann.

С точки зрения программиста, существует два существенных различия между использованием сокета домена Unix и сокета TCP/IP. Во-первых, адрес сокета – это путь в файловой системе, а не кортеж, содержащий имя сервера и порт. Во-вторых, узел, созданный в файловой системе для представления сокета, сохраняется после закрытия сокета и должен удаляться каждый раз при запуске сервера. Предыдущий пример эхо-сервера можно обновить для использования UDS, внеся несколько изменений в раздел настройки.

socket необходимо создать с семейством адресов AF_UNIX . Привязка сокета и управление входящими подключениями работает так же, как и с сокетами TCP/IP.

socket_echo_server_uds.py

import socket
import sys
import os

server_address  './uds_socket'

# Make sure the socket does not already exist
try:
    os.unlink(server_address)
except OSError:
    if os.path.exists(server_address):
        raise

# Create a UDS socket
sock  socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)

# Bind the socket to the address
print('starting up on {}'.format(server_address))
sock.bind(server_address)

# Listen for incoming connections
sock.listen(1)

while True:
    # Wait for a connection
    print('waiting for a connection')
    connection, client_address  sock.accept()
    try:
        print('connection from', client_address)

        # Receive the data in small chunks and retransmit it
        while True:
            data  connection.recv(16)
            print('received {!r}'.format(data))
            if data:
                print('sending data back to the client')
                connection.sendall(data)
            else:
                print('no data from', client_address)
                break

    finally:
        # Clean up the connection
        connection.close()

Также необходимо изменить настройку клиента для работы с UDS. Он должен предполагать, что узел файловой системы для сокета существует, поскольку сервер создает его путем привязки к адресу. Отправка и получение данных в клиенте UDS работает так же, как и в предыдущем клиенте TCP/IP.

socket_echo_client_uds.py

import socket
import sys

# Create a UDS socket
sock  socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)

# Connect the socket to the port where the server is listening
server_address  './uds_socket'
print('connecting to {}'.format(server_address))
try:
    sock.connect(server_address)
except socket.error as msg:
    print(msg)
    sys.exit(1)

try:

    # Send data
    message  b'This is the message.  It will be repeated.'
    print('sending {!r}'.format(message))
    sock.sendall(message)

    amount_received  0
    amount_expected  len(message)

    while amount_received < amount_expected:
        data  sock.recv(16)
        amount_received  len(data)
        print('received {!r}'.format(data))

finally:
    print('closing socket')
    sock.close()

Вывод программы в основном тот же, с соответствующими обновлениями адресной информации. Сервер показывает сообщения, полученные и отправленные обратно клиенту.

$ python3 socket_echo_server_uds.py
starting up on ./uds_socket
waiting for a connection
connection from
received b'This is the mess'
sending data back to the client
received b'age.  It will be'
sending data back to the client
received b' repeated.'
sending data back to the client
received b''
no data from
waiting for a connection

Клиент отправляет сообщение все сразу и постепенно получает обратно его части.

$ python3 socket_echo_client_uds.py
connecting to ./uds_socket
sending b'This is the message.  It will be repeated.'
received b'This is the mess'
received b'age.  It will be'
received b' repeated.'
closing socket

Разрешения

Поскольку сокет UDS представлен узлом в файловой системе, стандартные разрешения файловой системы могут использоваться для управления доступом к серверу.

$ ls -l ./uds_socket

srwxr-xr-x  1 dhellmann  dhellmann  0 Aug 21 11:19 uds_socket

$ sudo chown root ./uds_socket

$ ls -l ./uds_socket

srwxr-xr-x  1 root  dhellmann  0 Aug 21 11:19 uds_socket

Запуск клиента от имени пользователя, отличного от root , теперь приводит к ошибке, поскольку процесс не имеет разрешения на открытие сокета.

$ python3 socket_echo_client_uds.py

connecting to ./uds_socket
[Errno 13] Permission denied

Связь между родительскими и дочерними процессами

Функция socketpair () полезна для настройки сокетов UDS для межпроцессного взаимодействия в Unix. Он создает пару подключенных сокетов, которые могут использоваться для связи между родительским процессом и дочерним процессом после разветвления дочернего процесса.

socket_socketpair.py

import socket
import os

parent, child  socket.socketpair()

pid  os.fork()

if pid:
    print('in parent, sending message')
    child.close()
    parent.sendall(b'ping')
    response  parent.recv(1024)
    print('response from child:', response)
    parent.close()

else:
    print('in child, waiting for message')
    parent.close()
    message  child.recv(1024)
    print('message from parent:', message)
    child.sendall(b'pong')
    child.close()

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

$ python3 -u socket_socketpair.py

in parent, sending message
in child, waiting for message
message from parent: b'ping'
response from child: b'pong'