1 socket本地通信
socket原本是为网络通讯设计的,但后来在socket框架的基础上发展出了一种IPC(进程通信)机制,即UNIX Domain Socket,专门用来实现使用socket实现的本地进程通信。
本地通信的流程与使用的接口与基于TCP协议的网络通信模型相同,其大致流程如下:
- (1)调用socket()函数通信双方进程创建各自的socket文件;
- (2)定义并初始化服务器端进程的地址,并使用bind()函数将其与服务器端进程绑定;
- (3)调用listen()函数监听客户端进程请求;
- (4)客户端调用connect()函数,根据已明确的客户端进程地址,向服务器发送请求;
- (5)服务器端调用accept()函数,处理客户端进程的请求,若客户端与服务器端进程成功建立连接,则双方进程可开始通信;
- (6)通信双方以数据流的形式通过已创建的连接互相发送和接收数据,进行通信;
- (7)待通信结束后,通信双方各自调用close()函数关闭连接。
与socket网络通信不同的是,在本地通信中用到的套接字的结构体类型为socket sockaddr_un。
2 本地通信案例
【案例1】使用socket实现本地进程间通信。
dmclient.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 */
//创建客户端进程,成功返回0,出错返回小于0的errno
int cliConn(const char *paraName){int tempFd, tempLen, tempErr, tempRetVal;struct sockaddr_un tempSockUn;//创建本地套接字domainif ((tempFd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){return(-1);}//of if//使用自定义地址填充socket地址结构体memset(&tempSockUn, 0, sizeof(tempSockUn));tempSockUn.sun_family = AF_UNIX;sprintf(tempSockUn.sun_path, "%s%05d", CLI_PATH, getpid());tempLen = offsetof(struct sockaddr_un, sun_path) + strlen(tempSockUn.sun_path);unlink(tempSockUn.sun_path); //避免因文件已存在导致的bind()失败if (bind(tempFd, (struct sockaddr *)&tempSockUn, tempLen ) < 0) {tempRetVal = -2;goto errout;}//of if//使用服务器进程地址填充socket地址结构体memset(&tempSockUn, 0, sizeof(tempSockUn));tempSockUn.sun_family = AF_UNIX;strcpy(tempSockUn.sun_path, paraName);tempLen = offsetof(struct sockaddr_un, sun_path) + strlen(paraName);if (connect(tempFd, (struct sockaddr *)&tempSockUn, tempLen) < 0) {tempRetVal = -4;goto errout;}//of ifreturn(tempFd);
errout:tempErr = errno;close(tempFd);errno = tempErr;return(tempRetVal);
}//of cliConnint main(void) {int tempFd, tempDataLen;char tempBuf[1024];tempFd = cliConn("foo.socket"); //套接字文件为foo.socketif (tempFd < 0) { //容错处理switch (tempFd) {case -4:perror("connect"); break;case -3:perror("listen"); break;case -2:perror("bind"); break;case -1:perror("socket"); break;}//of switchexit(-1);}//of ifwhile (fgets(tempBuf, sizeof(tempBuf), stdin) != NULL) {write(tempFd, tempBuf, strlen(tempBuf));tempDataLen = read(tempFd, tempBuf, sizeof(tempBuf));write(STDOUT_FILENO, tempBuf, tempDataLen);}//of whileclose(fd);return 0;
}//of maindmserver.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>
#define QLEN 10
//创建服务器进程,成功返回0,出错返回小于0的errno
int servListen(const char *paraName) {int tempFd, tempLen, tempErr, tempRetVal;struct sockaddr_un tempSockUn;//创建本地domain套接字if ((tempFd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){return(-1);}//of if//删除套接字文件,避免因文件存在导致bind()绑定失败 unlink(paraName);//初始化套接字结构体地址memset(&tempSockUn, 0, sizeof(tempSockUn));tempSockUn.sun_family = AF_UNIX;strcpy(tempSockUn.sun_path, paraName);tempLen = offsetof(struct sockaddr_un, sun_path) + strlen(paraName);if (bind(tempFd, (struct sockaddr *)&tempSockUn, tempLen) < 0) {tempRetVal = -2;goto errout;}//of ifif (listen(tempFd, QLEN) < 0) { //告知内核这是一个服务器进程tempRetVal = -3;goto errout;}//of ifreturn(tempFd);
errout:tempErr = errno;close(tempFd);errno = tempErr;return(tempRetVal);
}//of servListenint servAccept(int paraListenfd, uid_t *paraUidPtr) {int tempCliFd, tempLen, tempErr, tempRetVal;time_t tempStaleTime;struct sockaddr_un tempSockUn;struct stat tempStatBuf;tempLen = sizeof(tempSockUn);if ((tempCliFd = accept(paraListenfd, (struct sockaddr *)&tempSockUn, &tempLen)) < 0){return(-1);}//of if//从调用地址获取客户端的uidtempLen -= offsetof(struct sockaddr_un, sun_path); //获取路径名长度tempSockUn.sun_path[tempLen] = 0; //为路径名字符串添加终止符if (stat(tempSockUn.sun_path, &tempStatBuf) < 0) {tempRetVal = -2;goto errout;}//of ifif (S_ISSOCK(tempStatBuf.st_mode) == 0) {tempRetVal = -3; //若返回值为-3,说明这不是一个socket文件goto errout;}//of ifif (paraUidPtr != NULL){*paraUidPtr = tempStatBuf.st_uid; //返回uid的调用者指针}//of if//到此成功获取路径名unlink(tempSockUn.sun_path);return(tempCliFd);
errout:tempErr = errno;close(tempCliFd);errno = tempErr;return(tempRetVal);
}//of servAcceptint main(void) {int tempListenFd, tempAcceptFd, tempDataLen, i;uid_t tempAcceptUid;char tempBuf[1024];tempListenFd = serv_listen("foo.socket");if (tempListenFd < 0) {switch (tempListenFd) {case -3:perror("listen"); break;case -2:perror("bind"); break;case -1:perror("socket"); break;}//of switchexit(-1);}//of iftempAcceptFd = serv_accept(tempListenFd, &tempAcceptUid);if (tempAcceptFd < 0) {switch (tempAcceptFd) {case -3:perror("not a socket"); break;case -2:perror("a bad filename"); break;case -1:perror("accept"); break;}//of switchexit(-1);}//of ifwhile (1) {r_again:tempDataLen = read(tempAcceptFd, tempBuf, 1024);if (tempDataLen == -1) {if (errno == EINTR){goto r_again;}//of if} else if (tempDataLen == 0) {printf("the other side has been closed.\n");break;}//of iffor (i = 0; i < n; i++){tempBuf[i] = toupper(tempBuf[i]);}//of for iwrite(tempAcceptFd, tempBuf, tempDataLen);}close(tempAcceptFd);close(tempListenFd);return 0;
}//of main