x86/debian gnu linux/gcc
UNIX Domain Socket是在socket的框架上发展出一种IPC机制,socket API原本是为网络通讯设计的。
UNIX Domain Socket是全双工的,API接口语义丰富,相比其它IPC机制有明显的优越性,目前已成为使用最广泛的IPC机制。
使用UNIX Domain Socket的过程和网络socket十分相似,也要先调用socket()创建一个socket文件描述符,address family指定为AF_UNIX,type可以选择SOCK_DGRAM或SOCK_STREAM,protocol参数仍然指定为0即可。UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,网络编程的”socket地址”是IP地址加端口号,而”UNIX Domain Socket的地址”是一个socket类型的文件在文件系统中的路径(isan one of ipc),这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。
2 IPC -- UNIX Domian socket code
(1) server.c(just one name of a process)
/*Filename: uds_ipc_server.c *Brife: Bind UNIX DOMAIN SOCKET's fd to socket address as server * When client connect, IPC will come true *Author: One fish *Date: 2014.9.10 Wed */ #include <stdlib.h> #include <stdio.h> #include <stddef.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/stat.h> #define FILE_PATH "./UDS_ser_file.socket" #define QLEN 10 int bind_unix_dmn_socket(const char *filename); int serv_accept(int listenfd, uid_t *uidptr); int main(void) { int listenfd; uid_t uidptr; listenfd = bind_unix_dmn_socket(FILE_PATH); if (listenfd > 0) { serv_accept(listenfd, &uidptr); } return 0; } //Bind UNIX Domain socket to an address int bind_unix_dmn_socket(const char *filename) { int fd, size; struct sockaddr_un un; memset(&un, 0, sizeof(un)); un.sun_family = AF_UNIX; strcpy(un.sun_path, filename); //Create one UNIX Domain socket if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { perror("socket error"); return -1; } size = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path); unlink(FILE_PATH); //Bind UNIX Domain socket to filename by fd if (bind(fd, (struct sockaddr *)&un, size) < 0) { perror("bind error"); close(fd); return -2; } printf("UNIX Domain socket(%d) bind to\" %s\" success\n", fd, filename); //Tell kernel we're a server if (listen(fd, QLEN) < 0) { fputs("Listen failed", stderr); close(fd); return -3; } return fd; } //Accept a client connect int serv_accept(int listenfd, uid_t *uidptr) { int clifd, len, err, rval; struct sockaddr_un un; struct stat statbuf; len = sizeof(un); //Accept client connect to if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0 ) { return -1; } len -= offsetof(struct sockaddr_un, sun_path); un.sun_path[len] = 0; if (stat(un.sun_path, &statbuf) < 0) { close(clifd); return -2; } if (S_ISSOCK(statbuf.st_mode) == 0) { close(clifd); return -3; } if (uidptr != NULL) { *uidptr = statbuf.st_uid; } printf("client %s which uid is %d connect to this server\n", un.sun_path, *uidptr); char str[]= "Wellcome to UNIX Domain socket of IPC"; send(clifd, str, strlen(str), 0); unlink(un.sun_path); return clifd; } |
与网络socket编程类似,在bind之后要listen,表示通过bind的地址(也就是socket文件)提供服务。
通过accept得到客户端地址也应该是一个socket文件,如果不是socket文件就返回错误码,如果是socket文件,在建立连接后这个文件就没有用了,调用unlink把它删掉,通过传出参数uidptr返回客户端程序的user id。
/*Filename: uds_ipc_client.c *Brife: Bind UNIX Domain socket's fd to socket address as client. * Then connect server via server's socket address *Author: One fish *Date: 2014.9.10 Wed */ #include <stdio.h> #include <stddef.h> #include <sys/stat.h> #include <sys/socket.h> #include <sys/un.h> #include <errno.h> #define CLI_PATH "/var/tmp/" #define SER_FILE_PATH "./UDS_ser_file.socket" int cli_conn(const char *filename); int main(void) { cli_conn(SER_FILE_PATH); return 0; } //Create a client endpoint and connect to a server. int cli_conn(const char *filename) { int fd, len, err, rval; struct sockaddr_un un; //Create a UNIX domain stream socket if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { return -1; } //Fill client's socket address memset(&un, 0, sizeof(un)); un.sun_family = AF_UNIX; sprintf(un.sun_path, "%s%05d", CLI_PATH, getpid()); len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path); unlink(un.sun_path); //Bind client's socket fd to its socket address if (bind(fd, (struct sockaddr *)&un, len) < 0) { close(fd); return -2; } //Fill socket address structure with server's address memset(&un, 0, sizeof(un)); un.sun_family = AF_UNIX; strcpy(un.sun_path, filename); len = offsetof(struct sockaddr_un, sun_path) + strlen(filename); //Connect to server if (connect(fd, (struct sockaddr *)&un, len) < 0) { close(fd); return -4; } char rvbuf[80] = "Server no say"; recv(fd, rvbuf, 78, 0); printf("Server's words:%s\n", rvbuf); return fd; } |
与网络socket编程不同的是,UNIX Domain Socket客户端一般要显式调用bind函数,而不依赖系统自动分配的地址。客户端bind一个自己指定的socket文件名的好处是,该文件名可以包含客户端的pid以便服务器区分不同的客户端。
compile and execute
in one terminal:
lly@debian:~/mydir/lly_books/linux_c_programming_osl/socket$gcc uds_ipc_server.c -o uds_ipc_server lly@debian:~/mydir/lly_books/linux_c_programming_osl/socket$./uds_ipc_server UNIX Domain socket(3) bind to" ./UDS_ser_file.socket"success
|
Now in another terminal:
lly@debian:~/mydir/lly_books/linux_c_programming_osl/socket$gcc uds_ipc_client.c -o uds_ipc_client lly@debian:~/mydir/lly_books/linux_c_programming_osl/socket$./uds_ipc_client |
The result:
uds_ipc_server: ![]()
uds_ipc_client: ![]() |