5高并发服务器:socket IPC
socket IPC
socket API原本是為網絡通訊設計的,但后來在socket的框架上發展出一種IPC機制,就是UNIX Domain Socket。雖然網絡socket也可用于同一臺主機的進程間通訊(通過loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率:不需要經過網絡協議棧,不需要打包拆包、計算校驗和、維護序號和應答等,只是將應用層數據從一個進程拷貝到另一個進程。這是因為,IPC機制本質上是可靠的通訊,而網絡協議是為不可靠的通訊設計的。UNIX Domain Socket也提供面向流和面向數據包兩種API接口,類似于TCP和UDP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不會丟失也不會順序錯亂。
UNIX Domain Socket是全雙工的,API接口語義豐富,相比其它IPC機制有明顯的優越性,目前已成為使用最廣泛的IPC機制,比如X Window服務器和GUI程序之間就是通過UNIX Domain Socket通訊的。
使用UNIX DomainSocket的過程和網絡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類型的文件在文件系統中的路徑,這個socket文件由bind()調用創建,如果調用bind()時該文件已存在,則bind()錯誤返回。
???以下程序將UNIXDomain socket綁定到一個地址。
size =offsetof(struct sockaddr_un,sun_path) + strlen(un.sun_path);
offsetof查看第二個參數在前者內的偏移量
案例說明:
server.c
| #include <stdlib.h> #include <stdio.h> #include <stddef.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> #include <ctype.h> ? #define QLEN 10 /* ?* Create a server endpoint of a connection. ?* Returns fd if all OK, <0 on error. ?*/ int serv_listen(const char *name) { ???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); ???unlink(name); /* in case it already exists */ ???/* fill in socket address structure */ ???memset(&un, 0, sizeof(un)); ???un.sun_family = AF_UNIX; ???strcpy(un.sun_path, name); ???len = offsetof(struct sockaddr_un, sun_path) + strlen(name); ???/* bind the name to the descriptor */ ???if (bind(fd, (struct sockaddr *)&un, len) < 0) { ???????rval = -2; ???????goto errout; ???} ???if (listen(fd, QLEN) < 0) { /* tell kernel we're a server */ ???????rval = -3; ???????goto errout; ???} ???return(fd); errout: ???err = errno; ???close(fd); ???errno = err; ???return(rval); } int serv_accept(int listenfd, uid_t *uidptr) { ???int clifd, len, err, rval; ???time_t staletime; ???struct sockaddr_un un; ???struct stat statbuf; ???len = sizeof(un); ???if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0) ???????return(-1); /* often errno=EINTR, if signal caught */ ???/* obtain the client's uid from its calling address */ ???len -= offsetof(struct sockaddr_un, sun_path); /* len of pathname */ ???un.sun_path[len] = 0; /* null terminate */ ???if (stat(un.sun_path, &statbuf) < 0) { ???????rval = -2; ???????goto errout; ???} ???if (S_ISSOCK(statbuf.st_mode) == 0) { ???????rval = -3; /* not a socket */ ???????goto errout; ???} ???if (uidptr != NULL) ???????*uidptr = statbuf.st_uid; /* return uid of caller */ ???unlink(un.sun_path); /* we're done with pathname now */ ???return(clifd); errout: ???err = errno; ???close(clifd); ???errno = err; ???return(rval); } int main(void) { ???int lfd, cfd, n, i; ???uid_t cuid; ???char buf[1024]; ???lfd = serv_listen("foo.socket"); ???if (lfd < 0) { ???????switch (lfd) { ???????????case -3:perror("listen"); break; ???????????case -2:perror("bind"); break; ???????????case -1:perror("socket"); break; ???????} ???????exit(-1); ???} ???cfd = serv_accept(lfd, &cuid); ???if (cfd < 0) { ???????switch (cfd) { ???????????case -3:perror("not a socket"); break; ???????????case -2:perror("a bad filename"); break; ???????????case -1:perror("accept"); break; ???????} ???????exit(-1); ???} ???while (1) { r_again: ???????n = read(cfd, buf, 1024); ???????if (n == -1) { ???????????if (errno == EINTR) ???????????????goto r_again; ???????} ???????else if (n == 0) { ???????????printf("the other side has been closed.\n"); ???????????break; ???????} ???????for (i = 0; i < n; i++) ???????????buf[i] = toupper(buf[i]); ???????write(cfd, buf, n); ???} ???close(cfd); ???close(lfd); ???return 0; } |
client.c
| #include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/socket.h> #include <sys/un.h> #include <errno.h> #define CLI_PATH "/var/tmp/" /* +5 for pid = 14 chars */ /* ?* Create a client endpoint and connect to a server. ?* Returns fd if all OK, <0 on error. ?*/ int cli_conn(const char *name) { ???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 socket address structure with our 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); /* in case it already exists */ ???if (bind(fd, (struct sockaddr *)&un, len) < 0) { ???????rval = -2; ???????goto errout; ???} ???/* fill socket address structure with server's address */ ???memset(&un, 0, sizeof(un)); ???un.sun_family = AF_UNIX; ???strcpy(un.sun_path, name); ???len = offsetof(struct sockaddr_un, sun_path) + strlen(name); ???if (connect(fd, (struct sockaddr *)&un, len) < 0) { ???????rval = -4; ???????goto errout; ???} ???return(fd); errout: ???err = errno; ???close(fd); ???errno = err; ???return(rval); } int main(void) { ???int fd, n; ???char buf[1024]; ???fd = cli_conn("foo.socket"); ???if (fd < 0) { ???????switch (fd) { ???????????case -4:perror("connect"); break; ???????????case -3:perror("listen"); break; ???????????case -2:perror("bind"); break; ???????????case -1:perror("socket"); break; ???????} ???????exit(-1); ???} ???while (fgets(buf, sizeof(buf), stdin) != NULL) { ???????write(fd, buf, strlen(buf)); ???????n = read(fd, buf, sizeof(buf)); ???????write(STDOUT_FILENO, buf, n); ???} ???close(fd); ???return 0; } |
?
總結
以上是生活随笔為你收集整理的5高并发服务器:socket IPC的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 去额尔古纳在哪坐客车?
- 下一篇: 传奇网站被劫持怎么修复