Unix Domain Sockets
Unix Domain Sockets
Unix Domain Sockets (auch Local Sockets oder AF_UNIX-Sockets) sind eine Form der Interprozesskommunikation (IPC) unter Unix-ähnlichen Betriebssystemen (Linux, macOS, BSD). Im Gegensatz zu Internet-Sockets (z. B. TCP/UDP über IPv4 oder IPv6) ermöglichen Unix Domain Sockets eine schnelle und sichere Kommunikation zwischen Prozessen auf demselben System.
---
Grundprinzip
Unix Domain Sockets verwenden das Dateisystem als Namensraum:
Jeder Socket wird durch einen Dateipfad (z. B. /tmp/socket) repräsentiert.
Prozesse können über diesen Pfad eine Verbindung aufbauen und bidirektional Daten austauschen – ähnlich wie über Netzwerk-Sockets, jedoch ohne Netzwerkprotokoll-Overhead.
+-------------+ +-------------+
| Prozess A | <---------> | Prozess B |
| (Client) | | (Server) |
+-------------+ +-------------+
\ /
\_________________/
Unix Domain Socket (/tmp/demo.sock)
---
Eigenschaften
- Kommunikation nur lokal (kein Routing über Netzwerke)
- Sehr geringe Latenz und hohe Geschwindigkeit
- Unterstützt Stream (SOCK_STREAM) und Datagram (SOCK_DGRAM)-Typen
- Nutzung von Dateisystemrechten zur Zugriffskontrolle
- Ermöglicht Dateideskriptor-Weitergabe (Advanced IPC)
---
Socket-Typen
| Typ | Beschreibung | Entspricht |
|---|---|---|
SOCK_STREAM |
zuverlässige, verbindungsorientierte Kommunikation | ähnlich wie TCP |
SOCK_DGRAM |
verbindungslos, Nachrichten-orientiert | ähnlich wie UDP |
SOCK_SEQPACKET |
sequenziell, aber paketorientiert | Hybridform |
---
Header und Adressstruktur
#include <sys/socket.h>
#include <sys/un.h>
Adresse einer Unix-Socket-Verbindung:
struct sockaddr_un {
sa_family_t sun_family; // AF_UNIX
char sun_path[108]; // Pfadname des Sockets
};
---
Beispiel 1: Server mit Unix Domain Stream Socket
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SOCKET_PATH "/tmp/unix_demo.sock"
int main() {
int server_fd, client_fd;
struct sockaddr_un addr;
char buffer[128];
unlink(SOCKET_PATH); // alten Socket-Pfad löschen
if ((server_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);
if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("bind");
exit(EXIT_FAILURE);
}
if (listen(server_fd, 5) == -1) {
perror("listen");
exit(EXIT_FAILURE);
}
printf("Server wartet auf Verbindung...\n");
client_fd = accept(server_fd, NULL, NULL);
if (client_fd == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
read(client_fd, buffer, sizeof(buffer));
printf("Nachricht vom Client: %s\n", buffer);
const char* reply = "Hallo vom Server!";
write(client_fd, reply, strlen(reply));
close(client_fd);
close(server_fd);
unlink(SOCKET_PATH); // Socket-Datei entfernen
return 0;
}
---
Beispiel 2: Client
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SOCKET_PATH "/tmp/unix_demo.sock"
int main() {
int sock;
struct sockaddr_un addr;
char buffer[128];
if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("connect");
exit(EXIT_FAILURE);
}
const char* message = "Hallo Server!";
write(sock, message, strlen(message));
int n = read(sock, buffer, sizeof(buffer) - 1);
buffer[n] = '\0';
printf("Antwort vom Server: %s\n", buffer);
close(sock);
return 0;
}
---
Beispiel 3: Datagram-Variante (SOCK_DGRAM)
Bei SOCK_DGRAM ist keine Verbindung nötig.
Client und Server tauschen Nachrichten ähnlich wie UDP aus.
Server:
int fd;
struct sockaddr_un addr;
char buf[100];
fd = socket(AF_UNIX, SOCK_DGRAM, 0);
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, "/tmp/server_dgram.sock");
unlink(addr.sun_path);
bind(fd, (struct sockaddr*)&addr, sizeof(addr));
struct sockaddr_un client;
socklen_t len = sizeof(client);
recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&client, &len);
printf("Empfangen: %s\n", buf);
sendto(fd, "Antwort vom Server", 18, 0, (struct sockaddr*)&client, len);
Client:
int fd;
struct sockaddr_un addr, srv;
char buf[100];
fd = socket(AF_UNIX, SOCK_DGRAM, 0);
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, "/tmp/client_dgram.sock");
unlink(addr.sun_path);
bind(fd, (struct sockaddr*)&addr, sizeof(addr));
srv.sun_family = AF_UNIX;
strcpy(srv.sun_path, "/tmp/server_dgram.sock");
sendto(fd, "Hallo Server", 12, 0, (struct sockaddr*)&srv, sizeof(srv));
recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
printf("Antwort: %s\n", buf);
---
Zugriffsschutz
Unix Domain Sockets nutzen das Dateisystem-Rechtesystem. Nur Prozesse mit ausreichenden Rechten (z. B. Besitzer, Gruppe, `chmod`-Berechtigungen) dürfen sich verbinden. Das macht sie sicherer als Netzwerk-Sockets für lokale Kommunikation.
---
Leistung
Da keine Protokollkonvertierung (IP, Routing, Checksummen etc.) notwendig ist, sind Unix Domain Sockets deutlich schneller als TCP/IP-Sockets auf demselben Rechner. Sie erreichen Übertragungsraten ähnlich wie anonyme Pipes oder Shared Memory, bieten aber zugleich vollwertige bidirektionale Kommunikation.
---
Unterschiede zu TCP/IP-Sockets
| Merkmal | Unix Domain Socket | TCP/IP Socket |
|---|---|---|
| Namensraum | Dateipfad | IP-Adresse + Port |
| Reichweite | nur lokal | systemübergreifend |
| Sicherheit | durch Dateirechte | durch Firewalls/Netzwerkebene |
| Geschwindigkeit | sehr hoch | langsamer (Netzwerk-Stack) |
| Protokoll | kein IP/Transport-Layer | TCP oder UDP |
| Debugging | via `ls`, `lsof` | via `netstat`, `ss` |
---
Einsatzgebiete
- Kommunikation zwischen Daemon und Client (z. B.
systemd,dbus) - Datenbanken (z. B. PostgreSQL, MySQL)
- Container-Kommunikation (z. B. Docker Socket:
/var/run/docker.sock) - Lokale IPC zwischen GUI-Programmen und Hintergrunddiensten
---
Vorteile
- Sehr schnell und effizient
- Sicherheit über UNIX-Dateisystemrechte
- Keine Netzwerkkonfiguration nötig
- Unterstützt alle Socket-APIs (send, recv, select, poll, etc.)
Nachteile
- Nur lokale Kommunikation
- Kein Routing über Netzwerk
- Dateipfade müssen verwaltet und ggf. gelöscht werden
---
Siehe auch
- Socket (Programmierung)
- TCP (Protokoll)
- UDP (Protokoll)
- Interprozesskommunikation (IPC)
- Named Pipe (FIFO)
- POSIX Threads
- Systemaufrufe unter Unix
---
Quellen
- Stevens, W. Richard: UNIX Network Programming, Vol. 1, Prentice Hall
- IEEE Std 1003.1 (POSIX) – Sockets API
- Linux Man Pages:
man 7 unix,man 2 socket - GNU C Library Documentation
- The Linux Programming Interface – Michael Kerrisk, 2010