Sockets de dominio Unix

Desde la perspectiva del programador hay dos diferencias esenciales entre usar un conector de dominio Unix y un conector TCP/IP. Primero la dirección del conector es una ruta en el sistema de archivos, en lugar de una tupla que contiene el nombre del servidor y el puerto. En segundo lugar, el nodo creado en el sistema de archivos para representar el conector persiste después de que es cerrado, y debe eliminarse cada vez que se inicie el servidor. El ejemplo del servidor de eco anterior se puede actualizar para usar UDS al hacer algunos cambios en la sección de configuración.

El socket necesita ser creado con la familia de direcciones AF_UNIX. Atar el conector y gestionar las conexiones entrantes funciona de la misma manera que con conectores 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()

La configuración del cliente también debe modificarse para que funcione con UDS. Este debe asumir que el nodo del sistema de archivos para el conector existe, ya que el servidor lo crea enlazando a la dirección. Enviar y recibir los datos funcionan de la misma manera en el cliente UDS que el cliente TCP/IP de antes.

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()

La salida del programa es casi la misma, con actualizaciones apropiadas para la información de la dirección. El servidor muestra los mensajes recibidos y devueltos al cliente.

$ 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

El cliente envía el mensaje de una vez y recibe partes de él de vuelta de forma incremental.

$ 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

Permisos

Dado que el conector UDS está representado por un nodo en el sistema de archivos, los permisos estándar del sistema de archivos se pueden utilizar para controlar el acceso al servidor.

$ 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

Ejecutar el cliente como un usuario distinto de root ahora da como resultado un error porque el proceso no tiene permiso para abrir el conector.

$ python3 socket_echo_client_uds.py

connecting to ./uds_socket
[Errno 13] Permission denied

Comunicar entre procesos padres e hijos

La función socketpair() es útil para configurar conectores UDS para la comunicación entre procesos bajo Unix. Crea un par de conectores conectados que pueden usarse para comunicarse entre un proceso padre y un proceso hijo después de que el hijo es bifurcado.

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()

De forma predeterminada, se crea un conector UDS, pero la persona que llama también puede pasar opciones de familia de direcciones, tipo de conector e incluso protocolo para controlar cómo se crean los conectores.

$ python3 -u socket_socketpair.py

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