《UNIX网络编程 卷1》 笔记: UNIX域协议

来源:互联网 发布:mysql运行原理 编辑:程序博客网 时间:2024/06/05 20:01

Unix域协议并不是一个实际的协议族,而是在单个主机上客户进程和服务器进程之间通信的一种方法。Unix域使用的套接字结构如下:

struct sockaddr_un {sa_family_t sun_family; /* AF_UNIX */char sun_path[108]; /* pathname */};
Unix域中标识客户和服务器的协议地址是普通文件系统中的路径名。我们可以创建一个Unix域套接字,然后调用bind绑定一个路径名,代码如下:

int main(int argc, char **argv){int sockfd;socklen_t len;struct sockaddr_un addr1, addr2;if (argc != 2)err_quit("usage: unixbind <pathname>");sockfd = Socket(AF_LOCAL, SOCK_STREAM, 0);unlink(argv[1]); /*调用bind之前必须确保路径名不存在*/bzero(&addr1, sizeof(addr1));addr1.sun_family = AF_LOCAL;strncpy(addr1.sun_path, argv[1], sizeof(addr1.sun_path) - 1);Bind(sockfd, (SA *)&addr1, SUN_LEN(&addr1));len = sizeof(addr2);Getsockname(sockfd, (SA *)&addr2, &len);printf("bound name = %s, returned len = %d\n", addr2.sun_path, len);exit(0);}
运行结果如下


可以看到绑定的路径名“/tmp/123”现在是一个套接字文件。

Unix域提供了一个特殊的函数socketpair,定义如下:

    int socketpair(int family, int type, int protocol, int sv[2]);   
参数family只能是AF_LOCALAF_UNIX,我们常使用这个函数指定type为SOCK_STREAM,protocol为0,在父子进程之间创建一个全双工的流管道。

使用Unix域套接字的原因主要有两点:

    1. 在一个主机上Unix域套接字通信速度往往比AF_INET域套接字快。

    2. Unix域套接字可用于同一个主机上两个不同的进程间传递描述符。

本节我们展示使用Unix域套接字的客户/服务器程序(忽略服务器和客户之间交互的str_cli、str_echo等函数)。

先定义两个使用的路径名:

#defineUNIXSTR_PATH"/tmp/unix.str"/* Unix domain stream */#defineUNIXDG_PATH"/tmp/unix.dg"/* Unix domain datagram */

使用Unix域TCP协议的回射客户程序

int main(int argc, char **argv){int sockfd;struct sockaddr_un servaddr;sockfd = Socket(AF_LOCAL, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sun_family = AF_LOCAL; /*UNIX域*/strcpy(servaddr.sun_path, UNIXSTR_PATH); /*服务器使用的路径名*/Connect(sockfd, (SA *)&servaddr, sizeof(servaddr));str_cli(stdin, sockfd);exit(0);}
使用Unix域TCP协议的回射服务器程序

void sig_chld(int);int main(int argc, char **argv){int listenfd, connfd;pid_t childpid;socklen_t clilen;struct sockaddr_un cliaddr, servaddr;listenfd = Socket(AF_LOCAL, SOCK_STREAM, 0);unlink(UNIXSTR_PATH);bzero(&servaddr, sizeof(servaddr));servaddr.sun_family = AF_LOCAL; strcpy(servaddr.sun_path, UNIXSTR_PATH);Bind(listenfd, (SA *)&servaddr, sizeof(servaddr));Listen(listenfd, LISTENQ);Signal(SIGCHLD, sig_chld);for ( ; ; ) {clilen = sizeof(cliaddr);if ((connfd = accept(listenfd, (SA *)&cliaddr, &clilen)) < 0) {if (errno == EINTR)continue;elseerr_sys("accept error");}if ((childpid = Fork()) == 0) {Close(listenfd);str_echo(connfd);exit(0);}Close(connfd);}}void sig_chld(int signo){pid_t pid;int stat;/*等待所有子进程终止*/while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)printf("child %d terminated\n", pid);return;}
使用Unix域UDP协议的回射客户程序

int main(int argc, char **argv){int sockfd;struct sockaddr_un cliaddr, servaddr;sockfd = Socket(AF_LOCAL, SOCK_DGRAM, 0);bzero(&cliaddr, sizeof(cliaddr));cliaddr.sun_family = AF_LOCAL;strcpy(cliaddr.sun_path, tmpnam(NULL));/*与TCP客户不同的是,当使用UNIX域数据报协议时,必须显式bind一个路径名*/Bind(sockfd, (SA *)&cliaddr, sizeof(cliaddr));bzero(&servaddr, sizeof(servaddr));servaddr.sun_family = AF_LOCAL;strcpy(servaddr.sun_path, UNIXDG_PATH);dg_cli(stdin, sockfd, (SA *)&servaddr, sizeof(servaddr));exit(0);}
注意:代码里的注释说明了Unix域的UDP客户和TCP客户之间的不同之处。

使用Unix域UDP协议的回射服务器程序

int main(int argc, char **argv){int sockfd;struct sockaddr_un servaddr, cliaddr;sockfd = Socket(AF_LOCAL, SOCK_DGRAM, 0);unlink(UNIXDG_PATH);bzero(&servaddr, sizeof(servaddr));servaddr.sun_family = AF_LOCAL;strcpy(servaddr.sun_path, UNIXDG_PATH);Bind(sockfd, (SA *)&servaddr, sizeof(servaddr));dg_echo(sockfd, (SA *)&cliaddr, sizeof(cliaddr));exit(0);}