Reusing UNIX domain socket (SO_REUSEADDR for AF_UNIX)
Unix domain sockets are a networkless version of Internet sockets.
They have several advantages:
- Unix domain sockets are files, so file-system permissions may be used for them
- when one end is closed (e.g. process exits),
SIGPIPEis delivered to another end
- performance may be up to 2x better
See details here.
Lack of SO_REUSEADDR
A socket file is created by
bind(2) call. If the file already exists,
EADDRINUSE is returned.
Unlike Internet sockets (
AF_INET), Unix domain sockets (
AF_UNIX) doesn’t have
SO_REUSEADDR, at least on Linux and BSD. The only way to reuse a socket file is to remove it with
There are two bad approaches to deal with this problem:
We could call
The problem is that if we run two instances of our process, the second one will silently remove socket used by the first one, instead of reporting a failure.
Also, there is a race here since the socket can be created by another process between
We could call
unlink()when the process exits instead.
The problem is that if our process crashes,
unlink()will not be called and we’ll have a dangling socket.
Using a lock file
One option is to use a lock file in addition to the socket file.
We’ll use a separate lock file and never call
unlink() on it. When a process is going to bind a socket, it first tries to acquire a lock:
If the lock can’t be acquired, it means that another process is holding the lock now, because kernel guarantees that the lock is released if owner process exits or crashes.
If the lock is successfully acquired, we can safely
unlink()the socket, because we’re the only owner and no race may occur.
#define SOCK_PATH "/tmp/socket" #define LOCK_PATH "/tmp/socket.lock" int server = socket(AF_UNIX, SOCK_STREAM, 0); if (server == -1) exit(1); struct sockaddr_un server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sun_family = AF_UNIX; strncpy(server_addr.sun_path, SOCK_PATH, sizeof(server_addr.sun_path)); // open lock file int lock_fd = open(LOCK_PATH, O_RDONLY | O_CREAT, 0600); if (lock_fd == -1) exit(1); // try to acquire lock int ret = flock(lock_fd, LOCK_EX | LOCK_NB); if (ret != 0) exit(1); // the lock is held by another process // remove socket file unlink(SOCK_PATH); // create new socket file ret = bind(server, (struct sockaddr *)&server_addr, sizeof(server_addr)); if (ret != 0) exit(1);
Using abstract namespace sockets
Another option is to use Linux-specific abstract namespace sockets.
To create an abstract namespace socket, set the first byte in the
sun_path field of the
unix(7). This socket will not be mapped to the filesystem, so it’s not possible to use filesystem permissions or remove it with
The advantage is that such a socket is automatically removed when the process exits, so there is no problem with socket reusing.