unix 进程间通信 -- 套接字域协议

2015-01-06 21:40:36   最后更新: 2015-01-07 11:22:11   访问数量:969




unix 域协议并不是一个实际的协议族,而是在单个主机上执行 C/S 通信的一种方式,是进程间通信的方法之一

unix 域协议提供两类套接字:字节流套接字和数据报套接字,分别类似于 TCP 和 UDP,他所使用的 API 几乎等同于网络 C/S 通信所使用的 API

与其他 IPC 方法相比,unix 域协议有以下优势:

  1. 与两端位于同一个主机的 TCP 套接字相比,速度快一倍
  2. 可以在同一主机的不同进程间传递描述符
  3. 域套接字会将客户凭证(用户ID和组ID)提供给服务器,从而可以提供额外的安全措施
struct sockaddr_un { sa_family_t sun_family; /* AF_LOCAL */ char sun_path[140]; /* null-terminated pathname */ }

 

定义于 sys/un.h

存放在sun_path数组中的路径名必须以空字符结尾,如果 sun_path 是一个空字符串,则等价于 IPv4 的 INADDR_ANY 或 IPv6 的 IN6ADDR_ANY_INIT

SUN_LEN 宏以一个指向 sockaddr_un 结构的指针作为参数,返回该结构的实际大小

  1. 由 bind 创建的路径名默认访问权限为 0777,并按照当前 umask 进行修正
  2. 与 unix 域套接字关联的路径名应为绝对路径
  3. 在 connect 调用中指定的路径名必须是一个当前绑定在某个打开的 unix 域套接字上的路径名,并且他们的套接字类型也必须是一致的(SOCK_STREAM 或 DGRAM_STREAM)
  4. unix 域字节流套接字与 TCP 套接字一样,为进程提供一个无记录边界的字节流接口
  5. 如果某个 unix 域字节流套接字的 connect 调用发现这个监听套接字的队列已满,调用就立即返回一个 ECONNREFUSED 错误(对于 TCP,如果监听套接字的队列已满,则忽略新到达的 SYN,而 TCP 连接发起端则将数次发送 SYN 进行重试)
  6. unix 域数据报套接字类似于 UDP 套接字:他们都提供一个保留记录边界的不可靠的数据报服务

server

/* * file: main.c * author: 龙泉居士 * date: 2015-01-07 */ #include "function/function.h" int main () { int connfd; pid_t childpid; socklen_t clilen; struct sockaddr_un cliaddr; int listenfd = get_listenfd(); while (1) { clilen = sizeof(cliaddr); if ((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen)) < 0) { if (errno == EINTR) continue; else { perror("accept"); exit(-1); } } if ((childpid = fork()) == 0) { close(listenfd); str_echo(connfd); return 0; } close(connfd); } }

 

/* * file: function.h * author: 龙泉居士 * date: 2015-01-07 */ #ifndef _FUNCTION_H_20150106_ #define _FUNCTION_H_20150106_ #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <sys/un.h> #include <unistd.h> #include <signal.h> #include <sys/socket.h> #define MAXLINE 1024 #define LISTENQ 1024 #ifndef UNIXSTR_PATH #define UNIXSTR_PATH "/tmp/unix.str" #endif ssize_t writen (int, const void *, size_t); int get_listenfd(); void str_echo (int); #endif

 

/* * file: function.c * author: 龙泉居士 * date: 2015-01-07 */ #include "function.h" void str_echo (int sockfd) { ssize_t n; char buf[MAXLINE]; while (1) { while ((n = read (sockfd, buf, 1)) > 0) { printf("n:%d\n", n); if (writen (sockfd, buf, n) < 0) return; sleep(1); } if (n < 0 && errno == EINTR) continue; else if (n < 0) { perror ("read error: "); return; } } } int get_listenfd() { struct sockaddr_un servaddr; int listenfd = socket(AF_LOCAL, SOCK_STREAM, 0); if (listenfd < 0) { perror("can't create socket file"); exit(-1); } unlink(UNIXSTR_PATH); memset(&servaddr, 0, sizeof(servaddr)); servaddr.sun_family = AF_LOCAL; memcpy(servaddr.sun_path, UNIXSTR_PATH, strlen(UNIXSTR_PATH)+1); if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr)) < 0) { perror("bind"); exit(-1); } signal(SIGCHLD, SIG_IGN); if (listen(listenfd, LISTENQ) < 0) { perror("listen"); exit(-1); } return listenfd; } ssize_t writen (int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; char *ptr; int i; ptr = (char *)vptr; nleft = n; for (i=0; i<n; ++i) { if (ptr[i] >= 'a' && ptr[i] <= 'z') ptr[i] += 'A'-'a'; } while (nleft > 0) { if ((nwritten = write (fd, ptr, nleft)) <= 0) { if (nwritten < 0 && errno == EINTR) nwritten = 0; else { perror ("write error: "); return -1; } } nleft -= nwritten; ptr += nwritten; } return n; }

 

client

/* * file: main.c * author: 龙泉居士 * date: 2015-01-07 */ #include "function/function.h" int main () { int sockfd = connect_serv(); str_cli (stdin, sockfd); return 0; }

 

/* * file: function.h * author: 龙泉居士 * date: 2015-01-07 */ #ifndef _FUNCTION_20150107_ #define _FUNCTION_20150107_ #include <stdio.h> #include <errno.h> #include <fcntl.h> #include <stdlib.h> #include <string.h> #include <sys/un.h> #include <unistd.h> #include <signal.h> #include <sys/socket.h> #define MAXLINE 1024 #ifndef UNIXSTR_PATH #define UNIXSTR_PATH "/tmp/unix.str" #endif int connect_serv(); void str_cli (FILE *, int); ssize_t writen (int, const void *, size_t); ssize_t readline (int, void *, size_t); #endif

 

/* * file: function.c * author: 龙泉居士 * date: 2015-01-07 */ #include "function.h" int connect_serv() { int sockfd; struct sockaddr_un servaddr; sockfd = socket(PF_UNIX, SOCK_STREAM, 0); if (sockfd < 0) { perror("socket"); exit(-1); } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sun_family = AF_UNIX; memcpy(servaddr.sun_path, UNIXSTR_PATH, strlen(UNIXSTR_PATH)+1); if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { perror("connect"); exit(-1); } return sockfd; } void str_cli (FILE *fp, int sockfd) { char sendline[MAXLINE], recvline[MAXLINE]; while (fgets (sendline, MAXLINE, fp) != NULL) { if (writen (sockfd, sendline, strlen(sendline)) < 0) return; if (readline (sockfd, recvline, MAXLINE) == 0) { printf ("str_cli: server terminated prematurely"); return; } fputs (recvline, stdout); } } ssize_t writen (int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ((nwritten = write (fd, ptr, nleft)) <= 0) { if (nwritten < 0 && errno == EINTR) nwritten = 0; else { perror ("write error: "); return -1; } } nleft -= nwritten; ptr += nwritten; } return n; } ssize_t readline (int fd, void *vptr, size_t maxlen) { ssize_t n, rc; char c, *ptr; ptr = vptr; for (n=1; n<maxlen; n++) { again: if ((rc = read(fd, &c, 1)) == 1) { *ptr++ = c; if (c == '\n') break; } else if (rc == 0) { *ptr = 0; return n-1; } else { if (errno == EINTR) goto again; return -1; } } *ptr = 0; return n; }

 

 






读书笔记      技术帖      linux      unix      network      unp      unix网络编程      tcp      udp      socket      龙潭书斋      进程间通信      技术分享      ipc      socketpair      pipe      流管道     


京ICP备15018585号