Unix Domain Sockets
UNIX domain sockets are used to communicate with processes running on the same machine. Although Internet domain sockets can be used for this same purpose, UNIX domain sockets are more efficient. UNIX domain sockets only copy data; they have no
protocol processing to perform, no network headers to add or remove, no checksums to calculate, no sequence numbers to generate, and no acknowledgements to send.
UNIX domain sockets provide both stream and datagram interfaces. The UNIX domain datagram service is reliable, however. Messages are neither lost nor delivered out of order. UNIX domain sockets are like a cross between sockets and pipes. You can
use the network-oriented socket interfaces with them, or you can use the socketpair function to create a pair of unnamed, connected, UNIX domain sockets.
socketpair
#include <sys/socket.h>
int socketpair(int domain, int type, int protocol, int sockfd[2]);
Naming Domain Sockets
Although the socketpair function creates sockets that are connected to each other, the individual sockets don’t have names. This means that they can’t be addressed by unrelated processes.
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* pathname */
};
The sun_path member of the sockaddr_un structure contains a pathname. When we bind an address to a UNIX domain socket, the system creates a file of type S_IFSOCK with the same name. This file exists only as a means of advertising the socket name to clients. The file can’t be opened or otherwise used for communication by applications. If the file already exists when we try to bind the same address, the bind request will fail. When we close the socket, this file is not automatically removed, so we need to
make sure that we unlink it before our application exits.
The client-server communication example based on UNIX domain sockets
server.c
#include "apue.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
#define PATH "/tmp/uds_x"
#define BACKLOG 10
#define UNIX_PATH_MAX 126
void
handler(int connfd) {
int nbytes;
char buffer[256];
nbytes = read(connfd, buffer, 256);
buffer[nbytes] = 0;
printf("MESSAGE FROM CLIENT: %s\n", buffer);
nbytes = snprintf(buffer, 256, "hello from the server");
write(connfd, buffer, nbytes);
close(connfd);
}
int
main() {
struct sockaddr_un address;
int sockfd, connfd;
socklen_t addrlen;
pid_t pid;
sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
if(sockfd < 0) {
err_sys("create socket failed");
}
unlink(PATH);
memset(&address, 0, sizeof(struct sockaddr_un));
address.sun_family = AF_UNIX;
snprintf(address.sun_path, UNIX_PATH_MAX, PATH);
if (bind(sockfd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0) {
err_sys("bind error");
}
if(listen(sockfd, BACKLOG) != 0) {
err_sys("listen error");
}
while((connfd = accept(sockfd,
(struct sockaddr *) &address,
&addrlen)) > -1) {
pid = fork();
if(pid == 0) {
/* now inside newly created connection handling process */
handler(connfd);
exit(0);
}
/* still inside server process */
close(connfd);
}
close(sockfd);
unlink(PATH);
exit(0);
}
client.c
#include "apue.h"
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#define PATH "/tmp/uds_x"
#define UNIX_PATH_MAX 126
int
main(void) {
struct sockaddr_un address;
int sockfd, nbytes;
char buffer[256];
sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
if(sockfd < 0) {
err_sys("create socket failed");
}
memset(&address, 0, sizeof(struct sockaddr_un));
address.sun_family = AF_UNIX;
snprintf(address.sun_path, UNIX_PATH_MAX, PATH);
if(connect(sockfd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0) {
err_sys("connect failed");
}
nbytes = snprintf(buffer, 256, "hello from a client");
write(sockfd, buffer, nbytes);
nbytes = read(sockfd, buffer, 256);
buffer[nbytes] = 0;
printf("MESSAGE FROM SERVER: %s\n", buffer);
close(sockfd);
exit(0);
}