1. Overview
In this article, we will identify what Unix Sockets are, how we can interact with them, and how we can connect to them in a practical use case example.
2. Introduction to Sockets
First, we need to establish a baseline understanding of what a socket is in the world of Linux. We’ll focus more on Unix Sockets here, but the underlying API for Network Sockets is very similar.
2.1. Sockets in Linux
A socket in Linux is a bidirectional communication pipe. Unlike standard FIFOs or pipes, work with sockets is done using the sockets interface as opposed to the file interface.
Let’s use two quick commands to learn more. The first command is nc, which is short for netcat. The netcat utility can be used for many tasks involving networking in Linux. For example, let’s use netcat to quickly create a Unix Socket:
$ nc -U /tmp/demo.sock -l
The -U parameter tells netcat to use a Unix Socket file, which we have specified. The -l parameter tells it to act as the server-side and listen on the specified socket for incoming connections.
Now, let’s use the lsof command to inspect the file created by netcat. The lsof command is a utility used to list and give information about files that are in use by processes. The -U option tells lsof to only list Unix Socket files, but this would list many sockets, and in our case, we want to focus on one in particular by specifying it directly as an argument:
$ lsof /tmp/demo.sock
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nc 3491531 admin 3u unix 0x0000000000000000 0t0 25487430 /tmp/demo.sock type=STREAM
We have some good info on the process that is using /tmp/demo.sock here, including the command, PID, and user. We see a field called FD. When Linux processes need to perform I/O operations, they do so by reading or writing to file descriptors. Common examples are stdin, stdout, and stderr, which map to the file descriptors 0, 1, and 2, respectively.
2.2. Socket Types
Let’s go deeper into the last string in the output of our previous lsof command, type=STREAM. Both Network and Unix Sockets have several socket types. They share two main types, SOCK_STREAM and SOCK_DGRAM.
Network Sockets using SOCK_STREAM will use TCP, while those using SOCK_DGRAM will use UDP. Since UDP is unreliable by definition, any process that requires reliable data transfer over a network socket should use a network socket of type SOCK_STREAM. However, when it comes to Unix Sockets, both types are reliable.
The difference between the two types for Unix Sockets is that the SOCK_DGRAM type preserves message boundaries but is connectionless. In contrast, the SOCK_STREAM type does not preserve message boundaries but is connection-oriented.
Since Linux 2.6.4, there is another type of Unix Socket called SOCK_SEQPACKET that both preserves message boundaries and is connection-oriented.
2.3. SOCK_STREAM vs. SOCK_DGRAM Connection Behavior
Let’s demonstrate the difference between the two main types of Unix Sockets. We can do this with our handy netcat command:
$ nc -U /tmp/demo.sock -u -l
The command is the same as the previous netcat command but with the addition of a lowercase -u. This extra parameter tells netcat we want a Unix Socket of type SOCK_DGRAM. Let’s check it with lsof:
$ lsof /tmp/demo.sock
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nc 55330 admin 3u unix 0x0000000000000000 0t0 678755 /tmp/demo.sock type=DGRAM
Now, we have our SOCK_DGRAM Unix Socket. Let’s connect to it and see how it behaves. We can fire up a netcat client in another terminal:
$ nc -U /tmp/demo.sock -u
If we don’t add the -u parameter, we’ll get an error telling us that the protocol is the wrong type for the server socket.
We can provide input to the terminal, and when we press enter, we can see it on the other side. As sockets are bidirectional, both sides can send and receive.
Now, we’ll kill the server-side. The client stays up, and we can continue typing away and ‘sending’ data. We don’t have any indication that the server-side is down – that’s because a SOCK_DGRAM Unix Socket is connectionless.
Now, if we stop the client as well and remove the -u parameter to start the netcat server and client with a Unix Socket of type SOCK_STREAM, we can perform the same test, and we’ll see that when the server-side closes the connection, our client-side notices and it is also closed.
2.4. Unix Sockets Permissions
Unix Sockets use the permissions of the directory they’re in. Therefore, the process must have write and execute permissions on the directory to create the socket file. The same is true for any process that wishes to connect to the socket.
3. Practical Use Case Example: mysql.sock and SSH Forwarding
Using the popular MySQL database as an example, we can understand the advantage of using Unix Sockets when possible. When using a Unix Socket as opposed to a Network Socket with MySQL, there’s a measurable performance win from not having to incur the overhead for TCP/IP.
If we decide to use a Unix Socket, perhaps for the performance benefits, we would still like to have the ability to manage our services remotely. We can see we have a Unix Socket for MySQL:
$ lsof /var/run/mysqld/mysqld.sock
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
mysqld 3230830 mysql 32u unix 0xffff8a6472b84000 0t0 24740620 /var/run/mysqld/mysqld.sock type=STREAM
On our admin laptop/desktop, we open up a terminal and run the following ssh command:
$ ssh -N -L 1234:/var/run/mysqld/mysqld.sock demo
The above command assumes we’re connecting to the same user – admin in this case – as the current session on our local system. It also assumes we have a .ssh/config file with the following entry where remote_host_ip is an IP address specific to the environment:
Host demo
Hostname <remote_host_ip>
Now, we open our favorite database client. For the connection in our database client, based on the ssh tunnel we created earlier, we’ll provide the connection info:
- Host: localhost
- Port: 1234
Now, when our database management client connects to port 1234 on our localhost, the connection is forwarded through the ssh tunnel and connected to the MySQL database through the Unix Socket.
4. Conclusion
In this article, we took a brief tour of Unix Sockets in Linux and learned how to interact with them through common Linux utilities. We also worked through a practical example of Unix Socket usage by MySQL and how to work with it remotely. We hope this was an interesting read and that you are now able to work with Unix Sockets more comfortably.