Автор оригинала: 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'