Unix Domain Sockets¶
From the programmer’s perspective there are two essential differences between using a Unix domain socket and an TCP/IP socket. First, the address of the socket is a path on the filesystem, rather than a tuple containing servername and port. Second, the node created in the filesystem to represent the socket persists after the socket is closed, and needs to be removed each time the server starts up. The echo server example from earlier can be updated to use UDS by making a few changes in the setup section.
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
The socket needs to be created with address family AF_UNIX.
# Create a UDS socket sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Binding the socket and managing the incomming connections works the same as with TCP/IP sockets.
# Bind the socket to the port print >>sys.stderr, 'starting up on %s' % server_address sock.bind(server_address) # Listen for incoming connections sock.listen(1) while True: # Wait for a connection print >>sys.stderr, 'waiting for a connection' connection, client_address = sock.accept() try: print >>sys.stderr, 'connection from', client_address # Receive the data in small chunks and retransmit it while True: data = connection.recv(16) print >>sys.stderr, 'received "%s"' % data if data: print >>sys.stderr, 'sending data back to the client' connection.sendall(data) else: print >>sys.stderr, 'no more data from', client_address break finally: # Clean up the connection connection.close()
The client setup also needs to be modified to work with UDS. It should assume the filesystem node for the socket exists, since the server creates it by binding to the address.
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 >>sys.stderr, 'connecting to %s' % server_address try: sock.connect(server_address) except socket.error, msg: print >>sys.stderr, msg sys.exit(1)
Sending and receiving data works the same way in the UDS client as the TCP/IP client from before.
try: # Send data message = 'This is the message. It will be repeated.' print >>sys.stderr, 'sending "%s"' % 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 >>sys.stderr, 'received "%s"' % data finally: print >>sys.stderr, 'closing socket' sock.close()
The program output is mostly the same, with appropriate updates for the address information. From the server:
$ python ./socket_echo_server_uds.py starting up on ./uds_socket waiting for a connection connection from received "This is the mess" sending data back to the client received "age. It will be" sending data back to the client received " repeated." sending data back to the client received "" no more data from waiting for a connection
and from the client:
$ python socket_echo_client_uds.py connecting to ./uds_socket sending "This is the message. It will be repeated." received "This is the mess" received "age. It will be" received " repeated." closing socket
Since the UDS socket is represented by a node on the filesystem, standard filesystem permissions can be used to control access to the server.
$ ls -l ./uds_socket srwxr-xr-x 1 dhellmann dhellmann 0 Sep 20 08:24 ./uds_socket $ sudo chown root ./uds_socket $ ls -l ./uds_socket srwxr-xr-x 1 root dhellmann 0 Sep 20 08:24 ./uds_socket
Running the client as a user other than root now results in an error because the process does not have permission to open the socket.
$ python socket_echo_client_uds.py connecting to ./uds_socket [Errno 13] Permission denied
Communication Between Parent and Child Processes¶
The socketpair() function is useful for setting up UDS sockets for interprocess communication under Unix. It creates a pair of connected sockets that can be used to communicate between a parent process and a child process after the child is forked.
import socket import os parent, child = socket.socketpair() pid = os.fork() if pid: print 'in parent, sending message' child.close() parent.sendall('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('pong') child.close()
By default, a UDS socket is created, but the caller can also pass address family, socket type, and even protocol options to control how the sockets are created.
$ python socket_socketpair.py in parent, sending message response from child: pong in child, waiting for message message from parent: ping