Unix domain socket 简介(进程间通信,进程通信)
Unix domain socket 又叫 IPC(inter-process communication 進程間通信) socket,用于實現(xiàn)同一主機上的進程間通信。socket 原本是為網(wǎng)絡(luò)通訊設(shè)計的,但后來在 socket 的框架上發(fā)展出一種 IPC 機制,就是 UNIX domain socket。雖然網(wǎng)絡(luò) socket 也可用于同一臺主機的進程間通訊(通過 loopback 地址 127.0.0.1),但是 UNIX domain socket 用于 IPC 更有效率:不需要經(jīng)過網(wǎng)絡(luò)協(xié)議棧,不需要打包拆包、計算校驗和、維護序號和應(yīng)答等,只是將應(yīng)用層數(shù)據(jù)從一個進程拷貝到另一個進程。這是因為,IPC 機制本質(zhì)上是可靠的通訊,而網(wǎng)絡(luò)協(xié)議是為不可靠的通訊設(shè)計的。
 UNIX domain socket 是全雙工的,API 接口語義豐富,相比其它 IPC 機制有明顯的優(yōu)越性,目前已成為使用最廣泛的 IPC 機制,比如 X Window 服務(wù)器和 GUI 程序之間就是通過 UNIX domain socket 通訊的。
 Unix domain socket 是 POSIX 標準中的一個組件,所以不要被名字迷惑,linux 系統(tǒng)也是支持它的。
下面通過一個簡單的 demo 來理解相關(guān)概念。程序分為服務(wù)器端和客戶端兩部分,它們之間通過 unix domain socket 進行通信。
服務(wù)器端程序
下面是一個非常簡單的服務(wù)器端程序,它從客戶端讀字符,然后將每個字符轉(zhuǎn)換為大寫并回送給客戶端:
#include <stdlib.h> #include <stdio.h> #include <stddef.h> #include <sys/socket.h> #include <sys/un.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <ctype.h> #define MAXLINE 80 char *socket_path = "server.socket"; int main(void) { struct sockaddr_un serun, cliun; socklen_t cliun_len; int listenfd, connfd, size; char buf[MAXLINE]; int i, n; if ((listenfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { perror("socket error"); exit(1); } memset(&serun, 0, sizeof(serun)); serun.sun_family = AF_UNIX; strcpy(serun.sun_path, socket_path); size = offsetof(struct sockaddr_un, sun_path) + strlen(serun.sun_path); unlink(socket_path); if (bind(listenfd, (struct sockaddr *)&serun, size) < 0) { perror("bind error"); exit(1); } printf("UNIX domain socket bound\n"); if (listen(listenfd, 20) < 0) { perror("listen error"); exit(1); } printf("Accepting connections ...\n"); while(1) { cliun_len = sizeof(cliun); if ((connfd = accept(listenfd, (struct sockaddr *)&cliun, &cliun_len)) < 0){ perror("accept error"); continue; } while(1) { n = read(connfd, buf, sizeof(buf)); if (n < 0) { perror("read error"); break; } else if(n == 0) { printf("EOF\n"); break; } printf("received: %s", buf); for(i = 0; i < n; i++) { buf[i] = toupper(buf[i]); } write(connfd, buf, n); } close(connfd); } close(listenfd); return 0; }簡單介紹一下這段代碼:
int socket(int family, int type, int protocol);使用 UNIX domain socket 的過程和網(wǎng)絡(luò) socket 十分相似,也要先調(diào)用 socket() 創(chuàng)建一個 socket 文件描述符.
family?指定為 AF_UNIX,使用 AF_UNIX 會在系統(tǒng)上創(chuàng)建一個 socket 文件,不同進程通過讀寫這個文件來實現(xiàn)通信。
type?可以選擇 SOCK_DGRAM 或 SOCK_STREAM。SOCK_STREAM 意味著會提供按順序的、可靠、雙向、面向連接的比特流。SOCK_DGRAM 意味著會提供定長的、不可靠、無連接的通信。
protocol?參數(shù)指定為 0 即可。
 UNIX domain socket 與網(wǎng)絡(luò) socket 編程最明顯的不同在于地址格式不同,用結(jié)構(gòu)體 sockaddr_un 表示,網(wǎng)絡(luò)編程的 socket 地址是 IP 地址加端口號,而 UNIX domain socket 的地址是一個 socket 類型的文件在文件系統(tǒng)中的路徑,這個 socket 文件由 bind() 調(diào)用創(chuàng)建,如果調(diào)用 bind() 時該文件已存在,則 bind() 錯誤返回。因此,一般在調(diào)用 bind() 前會檢查 socket 文件是否存在,如果存在就刪除掉。
 網(wǎng)絡(luò) socket 編程類似,在 bind 之后要 listen,表示通過 bind 的地址(也就是 socket 文件)提供服務(wù)。
 接下來必須用 accept() 函數(shù)初始化連接。accept() 為每個連接創(chuàng)立新的套接字并從監(jiān)聽隊列中移除這個連接。
客戶端程序
下面是客戶端程序,它接受用戶的輸入,并把字符串發(fā)送給服務(wù)器,然后接收服務(wù)器返回的字符串并打印:
#include <stdlib.h> #include <stdio.h> #include <stddef.h> #include <sys/socket.h> #include <sys/un.h> #include <errno.h> #include <string.h> #include <unistd.h> #define MAXLINE 80 char *client_path = "client.socket"; char *server_path = "server.socket"; int main() { struct sockaddr_un cliun, serun; int len; char buf[100]; int sockfd, n; if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){ perror("client socket error"); exit(1); } // 一般顯式調(diào)用bind函數(shù),以便服務(wù)器區(qū)分不同客戶端 memset(&cliun, 0, sizeof(cliun)); cliun.sun_family = AF_UNIX; strcpy(cliun.sun_path, client_path); len = offsetof(struct sockaddr_un, sun_path) + strlen(cliun.sun_path); unlink(cliun.sun_path); if (bind(sockfd, (struct sockaddr *)&cliun, len) < 0) { perror("bind error"); exit(1); } memset(&serun, 0, sizeof(serun)); serun.sun_family = AF_UNIX; strcpy(serun.sun_path, server_path); len = offsetof(struct sockaddr_un, sun_path) + strlen(serun.sun_path); if (connect(sockfd, (struct sockaddr *)&serun, len) < 0){ perror("connect error"); exit(1); } while(fgets(buf, MAXLINE, stdin) != NULL) { write(sockfd, buf, strlen(buf)); n = read(sockfd, buf, MAXLINE); if ( n < 0 ) { printf("the other side has been closed.\n"); }else { write(STDOUT_FILENO, buf, n); } } close(sockfd); return 0; }與網(wǎng)絡(luò) socket 編程不同的是,UNIX domain socket 客戶端一般要顯式調(diào)用 bind 函數(shù),而不依賴系統(tǒng)自動分配的地址。客戶端 bind 一個自己指定的 socket 文件名的好處是,該文件名可以包含客戶端的 pid 等信息以便服務(wù)器區(qū)分不同的客戶端。
運行上面的程序
分別把服務(wù)器端程序和客戶端程序保存為 server.c 和 client.c 文件,并編譯:
$ gcc server.c -o server $ gcc client.c -o client先啟動服務(wù)器端程序,然后啟動客戶端程序輸入字符串并回車:
還不錯,客戶端得到了服務(wù)器端返回的大寫字符串。接下來看看當前目錄下的文件:
總結(jié)
Unix domain socket 主要用于同一主機上的進程間通信。與主機間的進程通信不同,它不是通過 "IP地址 + TCP或UDP端口號" 的方式進程通信,而是使用 socket 類型的文件來完成通信,因此在穩(wěn)定性、可靠性以及效率方面的表現(xiàn)都很不錯。
總結(jié)
以上是生活随笔為你收集整理的Unix domain socket 简介(进程间通信,进程通信)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: matlab实现整数规划/01规划
- 下一篇: 《论文笔记》Multi-UAV Coll
