UNIX 套接字可在用一台机器上实现进程间通信,因为 UNIX 域套接字仅仅复制数据,不执行协议处理,不需要添加或删除网络报头,无需验证和,不产生顺序号,无需发送确认报文,比因特网域套接字的效率更高。UNIX域套接字提供流和数据报两种接口,UNIX域数据报服务是可靠的,既不会丢失消息也不会传递出错。UNIX域套接字是套接字和管道之间的混合物。为了创建一对非命名的,相互连接的 UNXI 域套接字,用户可以使用socketopair函数。其实现如下:
#include <sys/socket.h> int socketpair(int domain, int type, int protocol, int sockfd[2]); /* 返回值:若成功则返回0,出错则返回-1 */
#include "apue.h" #include <sys/socket.h> /* * Return a full-duplex "stream" pipe (a UNIX domain socket) * with the two file descriptors returned in fd[0] and fd[1]. */ int s_pipe(int fd[2]) { return(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)); }
命名 UNIX 域套接字也是针对没有亲缘关系进程之间的通信,它的地址结构和因特网的地址结构不同,其地址结构如下:
struct sockaddr_un{ sa_family_t sun_family; /* AF_UNIX */ char sun_path[108]; /* pathname */ };
#include "apue.h" #include <sys/socket.h> #include <sys/un.h> int main(void) { int fd, size; struct sockaddr_un un; un.sun_family = AF_UNIX; strcpy(un.sun_path, "foo.socket"); if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) err_sys("socket failed"); size = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path); if(bind(fd, (struct sockaddr *)&un, size) < 0) err_sys("bind failed"); printf("UNIX domain socket bound\n"); exit(0); }
服务器进程可以使用标准 bind、listen 和 accept 函数,为客户进程安排一个唯一的 UNIX 域连接。客户进程使用 connect 与服务器进程关联;服务器进程接受了 connect 请求后,在服务器进程和客户进程之间就存在了唯一连接。以下是 UNIX 域套接字唯一连接的实现:这些函数可以套用在前面介绍《基于 socket 的编程》的框架中。
#include "apue.h" #include <sys/socket.h> #include <sys/un.h> #include <errno.h> #define QLEN 10 /* * Create a server endpoint of a connection. * Return 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); }
#include "apue.h" #include <sys/socket.h> #include <sys/un.h> #include <time.h> #include <errno.h> #define STALE 30 /* client's name can't be older than this (sec) */ /* * Wait for a client connection to arrive, and accept it. * We also obtain the client's usr ID from the pathname * that it must bind before calling us. * Returns new fd if all ok, <0 on error */ 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; } #ifdef S_ISSOCK /* not defined fro SVR4 */ if(S_ISSOCK(statbuf.st_mode) == 0) { rval = -3; /* not a socket */ goto errout; } #endif if((statbuf.st_mode & (S_IRWXG | S_IRWXO)) || (statbuf.st_mode & S_IRWXU) != S_IRWXU) { rval = -4; /* is not rwx------ */ goto errout; } staletime = time(NULL) - STALE; if(statbuf.st_atime < staletime || statbuf.st_ctime < staletime || statbuf.st_mtime < staletime) { rval = -5; /* i-node is too old */ 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); }
#include "apue.h" #include <sys/socket.h> #include <sys/un.h> #include <errno.h> #define CLI_PATH "/var/tmp/" /* +5 fro pid = 14 chars */ #define CLI_PERM S_IRWXU /* rwx for user only */ /* * 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_STREM, 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 exits */ if(bind(fd, (struct sockaddr *)&un, len) < 0) { rval = -2; goto errout; } if(chmod(un.sun_path, CLI_PERM) < 0) { rval = -3; 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); }
《UNIX高级环境编程》