Socket编程实践(13)

UNIX域协议

UNIX域套接字与TCP相比,在同一台主机上,UNIX域套接字更有效率,几乎是TCP的两倍(由于UNIX域套接字不需要经过网络协议栈,不需要打包/拆包,计算校验和,维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程,而且UNIX域协议机制本质上就是可靠的通讯,而网络协议是为不可靠的通讯设计的).

UNIX域套接字可以在同一台主机上各进程之间传递文件描述符;

UNIX域套接字与传统套接字的区别是用路径名来表示协议族的描述;

UNIX域套接字也提供面向流和面向数据包两种API接口,类似于TCP和UDP,但是面向消息的UNIX套接字也是可靠的,消息既不会丢失也不会顺序错乱。

使用UNIX域套接字的过程和网络socket十分相似,也要先调用socket创建一个socket文件描述符,family指定为AF_UNIX,type可以选择SOCK_DGRAM/SOCK_STREAM;

UNIX域套接字地址结构:

#define UNIX_PATH_MAX 108struct sockaddr_un{sa_family_t sun_family;/* AF_UNIX */charsun_path[UNIX_PATH_MAX]; /* pathname */};

基于UNIX域套接字的echo-server/client程序

/**Server端**/void echoServer(int sockfd);int main(){signal(SIGCHLD, sigHandlerForSigChild);int listenfd = socket(AF_UNIX, SOCK_STREAM, 0);if (listenfd == -1)err_exit("socket error");char pathname[] = "/tmp/test_for_unix";unlink(pathname);struct sockaddr_un servAddr;servAddr.sun_family = AF_UNIX;strcpy(servAddr.sun_path, pathname);if (bind(listenfd, (struct sockaddr *)&servAddr, sizeof(servAddr)) == -1)err_exit("bind error");if (listen(listenfd, 128) == -1)err_exit("listen error");while (true){int connfd = accept(listenfd, NULL, NULL);if (connfd == -1)err_exit("accept error");pid_t pid = fork();if (pid == -1)err_exit("fork error");else if (pid > 0)close(connfd);else if (pid == 0){close(listenfd);echoServer(connfd);close(connfd);exit(EXIT_SUCCESS);}}}void echoServer(int sockfd){char buf[BUFSIZ];while (true){memset(buf, 0, sizeof(buf));int recvBytes = read(sockfd, buf, sizeof(buf));if (recvBytes < 0){if (errno == EINTR)continue;elseerr_exit("read socket error");}else if (recvBytes == 0){cout << "client connect closed…" << endl;break;}cout << buf ;if (write(sockfd, buf, recvBytes) == -1)err_exit("write socket error");}}/**Client端代码**/void echoClient(int sockfd);int main(){int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);if (sockfd == -1)err_exit("socket error");char pathname[] = "/tmp/test_for_unix";struct sockaddr_un servAddr;servAddr.sun_family = AF_UNIX;strcpy(servAddr.sun_path, pathname);if (connect(sockfd, (struct sockaddr *)&servAddr, sizeof(servAddr)) == -1)err_exit("connect error");echoClient(sockfd);}void echoClient(int sockfd){char buf[BUFSIZ] = {0};while (fgets(buf, sizeof(buf), stdin) != NULL){if (write(sockfd, buf, strlen(buf)) == -1)err_exit("write socket error");memset(buf, 0, sizeof(buf));int recvBytes = read(sockfd, buf, sizeof(buf));if (recvBytes == -1){if (errno == EINTR)continue;elseerr_exit("read socket error");}cout << buf ;memset(buf, 0, sizeof(buf));}}UNIX域套接字编程注意点

1.bind成功将会创建一个文件,权限为0777&~umask

2.sun_path最好用一个/tmp目录下的文件的绝对路径,而且server端在指定该文件之前首先要unlink一下;

3.UNIX域协议支持流式套接口(需要处理粘包问题)与报式套接口(基于数据报)

4.UNIX域流式套接字connect发现监听队列满时,会立刻返回一个ECONNREFUSED,这和TCP不同,如果监听队列满,,会忽略到来的SYN,这导致对方重传SYN。

传递文件描述符

socketpair

#include <sys/types.h>#include <sys/socket.h>int socketpair(int domain, int type, int protocol, int sv[2]);

创建一个全双工的流管道

参数:

domain:协议家族,可以使用AF_UNIX(AF_LOCAL)UNIX域协议,而且在Linux上,该函数也就只支持这一种协议;

type:套接字类型,可以使用SOCK_STREAM

protocol:协议类型,一般填充为0;

sv:返回的套接字对;

socketpair函数跟pipe函数是类似:只能在具有亲缘关系的进程间通信,但pipe创建的匿名管道是半双工的,而socketpair可以认为是创建一个全双工的管道。

可以使用socketpair创建返回的套接字对进行父子进程通信,如下例:

int main(){int sockfds[2];if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds) == -1)err_exit("socketpair error");pid_t pid = fork();if (pid == -1)err_exit("fork error");// 父进程, 只负责数据的打印else if (pid > 0){close(sockfds[1]);int iVal = 0;while (true){cout << "value = " << iVal << endl;write(sockfds[0], &iVal, sizeof(iVal));read(sockfds[0], &iVal, sizeof(iVal));sleep(1);}}// 子进程, 只负责数据的更改(+1)else if (pid == 0){close(sockfds[0]);int iVal = 0;while (read(sockfds[1], &iVal, sizeof(iVal)) > 0){++ iVal;write(sockfds[1], &iVal, sizeof(iVal));}}}陪我们走过一段别人无法替代的记忆。

Socket编程实践(13)

相关文章:

你感兴趣的文章:

标签云: