What is a Unix Domain Socket (UDS) #
Unix Domain Socket (UDS) is an inter-process communication (IPC) mechanism in Linux/Unix systems. Unlike traditional TCP/IP sockets, UDS is used exclusively for communication between processes on the same host, bypassing the network protocol stack, which results in higher efficiency and lower latency. It communicates through a special file in the filesystem (typically a socket file), providing reliable bidirectional data transmission.
UDS supports two communication modes:
- Stream (SOCK_STREAM): Similar to TCP, it provides connection-oriented, reliable data stream transmission.
- Datagram (SOCK_DGRAM): Similar to UDP, it provides connectionless, unreliable datagram transmission.
Advantages of UDS #
- High Performance: By avoiding the network protocol stack, UDS offers higher communication efficiency than TCP/IP sockets, especially for high-frequency, small-data scenarios.
- Security: UDS leverages filesystem permissions, allowing access control through Linux file permissions, enhancing security.
- Simplicity: No need to configure IP addresses or ports; only a file path is required.
- Versatility: In addition to regular data, UDS supports transferring file descriptors, credentials, and more.
Use Cases for UDS #
UDS is widely used in Linux systems for inter-process communication, such as:
- Local Service Communication: For example, communication between database clients and servers (e.g., MySQL).
- System Services: Services like D-Bus and X11 use UDS for efficient communication.
- Microservices Architecture: Communication between microservices on the same host to reduce network overhead.
Simple Example: Using UDS for Communication #
Below is a simple C language example demonstrating how to use UDS for communication between a client and a server. The server listens for client messages and responds with a confirmation.
Server Code #
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#define SOCKET_PATH "/tmp/my_uds_socket"
#define BUFFER_SIZE 256
int main() {
int server_fd, client_fd;
struct sockaddr_un server_addr, client_addr;
char buffer[BUFFER_SIZE];
socklen_t client_len = sizeof(client_addr);
// Create UDS socket
server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("Socket creation failed");
exit(1);
}
// Set server address
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
// Bind socket
unlink(SOCKET_PATH); // Remove old socket file
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("Bind failed");
exit(1);
}
// Listen for connections
if (listen(server_fd, 5) == -1) {
perror("Listen failed");
exit(1);
}
printf("Server listening on %s\n", SOCKET_PATH);
// Accept client connection
client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
if (client_fd == -1) {
perror("Accept failed");
exit(1);
}
// Receive and respond to messages
while (1) {
int len = recv(client_fd, buffer, BUFFER_SIZE, 0);
if (len <= 0) break;
buffer[len] = '\0';
printf("Received: %s\n", buffer);
const char *response = "Message received!";
send(client_fd, response, strlen(response), 0);
}
// Cleanup
close(client_fd);
close(server_fd);
unlink(SOCKET_PATH);
return 0;
}
Client Code #
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#define SOCKET_PATH "/tmp/my_uds_socket"
#define BUFFER_SIZE 256
int main() {
int client_fd;
struct sockaddr_un server_addr;
char buffer[BUFFER_SIZE];
// Create UDS socket
client_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (client_fd == -1) {
perror("Socket creation failed");
exit(1);
}
// Set server address
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
// Connect to server
if (connect(client_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("Connect failed");
exit(1);
}
// Send message
const char *message = "Hello, UDS Server!";
send(client_fd, message, strlen(message), 0);
// Receive server response
int len = recv(client_fd, buffer, BUFFER_SIZE, 0);
buffer[len] = '\0';
printf("Server response: %s\n", buffer);
// Cleanup
close(client_fd);
return 0;
}
How to Run #
- Compile the server and client code:
gcc server.c -o server gcc client.c -o client
- Run the server:
./server
- In another terminal, run the client:
./client
Upon running, the server will receive the “Hello, UDS Server!” message from the client and respond with “Message received!”.
Notes #
- File Path: Ensure the
SOCKET_PATH
has appropriate permissions and is not occupied by other processes. - Cleanup: The server should remove old socket files (using
unlink
) before starting to avoid binding failures. - Error Handling: In production, implement robust error handling to manage connection interruptions or data anomalies.
Conclusion #
Unix Domain Sockets are an efficient and reliable tool for inter-process communication in Linux systems, particularly suited for low-latency, high-security local communication scenarios. With simple file path configuration and robust filesystem permission support, UDS plays a critical role in system development. I hope this blog helps you better understand and utilize UDS!