Unix 域套接字是一種client和server在單主機上的 IPC 方法。Unix 域套接字不運行協議處理,不需要加入或刪除網絡報頭,無需驗證和,不產生順序號,無需發送確認報文,比因特網域套接字的效率更高。Unix 域套接字提供字節流(相似於 TCP)和數據報(相似於 UDP)兩種接口,UNIX域數據報服務是可靠的,既不會丟失消息也不會傳遞出錯。UNIX域套接字是套接字和管道之間的混合物。編程
地址結構:數組
struct sockaddr_un{ sa_family_t sun_family; /* AF_UNIX */ char sun_path[108]; /* pathname */ };
/* 建立一個Unix域套接字,並bind一個路徑名 */ #include <sys/socket.h> #include <sys/un.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <stddef.h> extern void err_sys(const char *, ...); extern void err_quit(const char *, ...); int main(int argc, char **argv) { int sockfd, size; socklen_t len; struct sockaddr_un addr1, addr2; if(argc != 2) err_quit("usage: %s <pathname>", argv[0]); bzero(&addr1, sizeof(addr1)); addr1.sun_family = AF_UNIX; strncpy(addr1.sun_path, argv[1], sizeof(addr1.sun_path)-1); /* 建立一個Unix域套接字 */ if( (sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) err_sys("socket error"); /* 若路徑名在文件系統已存在,則bind會出錯;因此先調用unlink刪除要綁定的路徑名,防止bind出錯 */ unlink(argv[1]); /* 將路徑名bind綁定到該套接字上 */ size = offsetof(struct sockaddr_un, sun_path) + strlen(addr1.sun_path); if(bind(sockfd, (struct sockaddr *)&addr1, size) < 0) err_sys("bind error"); /* 顯示已綁定的路徑名 */ len = sizeof(addr2); getsockname(sockfd, (struct sockaddr *)&addr2, &len); printf("bound name = %s, returned len = %d\n", addr2.sun_path, len); exit(0); }
$ ./main /tmp/sock bound name = /tmp/sock, returned len = 12 /*當該路徑名存在,且不使用unlink函數時,會出現下面提示*/ $ ./main /tmp/sock bind error: Address already in use
爲了建立一對非命名的,相互鏈接的 UNXI 域套接字,用戶可以使用socketopair函數。事實上現例如如下:網絡
#include <sys/socket.h> int socketpair(int domain, int type, int protocol, int sockfd[2]); /* 返回值:若成功則返回0,出錯則返回-1 */ /* 說明 * 參數 domain 必須是 AF_LOCAL 或 AF_UNIX,protocol 必須爲 0,type 可以是 SOCK_STREAM 或 SOCK_DGRAM,新建立的兩個套接字描寫敘述符做爲sockfd[0]和sockfd[1]返回;
與因特網域套接字相比,Unix 域套接字有下面的差異:dom
server程序:socket
#include <sys/socket.h> #include <sys/wait.h> #include <sys/un.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <stddef.h> #include <signal.h> #include <errno.h> #define QLEN 1024 typedef void Sigfunc(int); extern void err_sys(const char *, ...); extern void err_quit(const char *, ...); extern void str_echo(int); static Sigfunc *MySignal(int signo, Sigfunc *func); static Sigfunc *M_signal(int signo, Sigfunc *func); static void sig_chld(int); int main(int argc, char **argv) { int sockfd, conndfd, size; socklen_t len; pid_t childpid; struct sockaddr_un cliaddr, servaddr; if(argc != 2) err_quit("usage: %s <pathname>", argv[0]); bzero(&servaddr, sizeof(servaddr)); servaddr.sun_family = AF_UNIX; strcpy(servaddr.sun_path, argv[1]); /* 建立一個Unix域套接字 */ if( (sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) err_sys("socket error"); /* 若路徑名在文件系統已存在,則bind會出錯;因此先調用unlink刪除要綁定的路徑名,防止bind出錯 */ unlink(argv[1]); /* 將路徑名bind綁定到該套接字上 */ size = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path); if(bind(sockfd, (struct sockaddr *)&servaddr, size) < 0) err_sys("bind error"); /* 監聽套接字 */ if(listen(sockfd, QLEN) < 0) { close(sockfd); err_sys("listen error"); } /* 信號處理 */ MySignal(SIGCHLD, sig_chld); for( ; ;) { len = sizeof(cliaddr); if( (conndfd = accept(sockfd, (struct sockaddr *)&cliaddr, &len)) < 0) { if(errno == EINTR) continue; else err_sys("accept error"); } } if( (childpid = fork()) == 0) { close(sockfd); str_echo(conndfd); exit(0); } close(conndfd); } void sig_chld(int signo) { pid_t pid; int stat; while( (pid = waitpid(-1, &stat, WNOHANG)) > 0) printf("child %d terminated\n", pid); return; } static Sigfunc *MySignal(int signo, Sigfunc *func) { Sigfunc *sigfunc; if( (sigfunc = M_signal(signo, func)) == SIG_ERR) err_sys("signal error"); return (sigfunc); } static Sigfunc *M_signal(int signo, Sigfunc *func) { struct sigaction act, oact; /* 設置信號處理函數 */ act.sa_handler = func; /* 初始化信號集 */ sigemptyset(&act.sa_mask); act.sa_flags = 0; if(signo == SIGALRM) {/* 如果SIGALRM信號,則系統不會本身主動從新啓動 */ #ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT; #endif } else {/* 其他信號設置爲系統會本身主動從新啓動 */ #ifdef SA_RESTART act.sa_flags |= SA_RESTART; #endif } /* 調用 sigaction 函數 */ if(sigaction(signo, &act, &oact) < 0) return(SIG_ERR); return(oact.sa_handler); }
#include <sys/socket.h> #include <sys/un.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> extern void err_sys(const char *, ...); extern void err_quit(const char *, ...); extern void str_cli(FILE *, int); int main(int argc, char **argv) { int sockfd; struct sockaddr_un servaddr; if(argc != 2) err_quit("usage: %s <pathname>", argv[0]); if( (sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) err_sys("socket error"); bzero(&servaddr, sizeof(servaddr)); servaddr.sun_family = AF_UNIX; strcpy(servaddr.sun_path, argv[1]); int err; err = connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); if( err < 0) err_sys("connect error"); str_cli(stdin, sockfd); /* do it all */ exit(0); }
《Unix 網絡編程》函數