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#include #include #include #include #include #include #include #include #define CLI_PATH "/var/tmp/" //创建客户端进程,成功返回0,出错返回小于0的errno int cliConn(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 //使用自定义地址填充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 if return(tempFd); errout: tempErr = errno; close(tempFd); errno = tempErr; return(tempRetVal); }//of cliConn int main(void) { int tempFd, tempDataLen; char tempBuf[1024]; tempFd = cliConn("foo.socket"); //套接字文件为foo.socket if (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 switch exit(-1); }//of if while (fgets(tempBuf, sizeof(tempBuf), stdin) != NULL) { write(tempFd, tempBuf, strlen(tempBuf)); tempDataLen = read(tempFd, tempBuf, sizeof(tempBuf)); write(STDOUT_FILENO, tempBuf, tempDataLen); }//of while close(fd); return 0; }//of main dmserver.c #include #include #include #include #include #include #include #include #include #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 if if (listen(tempFd, QLEN) < 0) { //告知内核这是一个服务器进程 tempRetVal = -3; goto errout; }//of if return(tempFd); errout: tempErr = errno; close(tempFd); errno = tempErr; return(tempRetVal); }//of servListen int 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 //从调用地址获取客户端的uid tempLen -= offsetof(struct sockaddr_un, sun_path); //获取路径名长度 tempSockUn.sun_path[tempLen] = 0; //为路径名字符串添加终止符 if (stat(tempSockUn.sun_path, &tempStatBuf) < 0) { tempRetVal = -2; goto errout; }//of if if (S_ISSOCK(tempStatBuf.st_mode) == 0) { tempRetVal = -3; //若返回值为-3,说明这不是一个socket文件 goto errout; }//of if if (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 servAccept int 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 switch exit(-1); }//of if tempAcceptFd = 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 switch exit(-1); }//of if while (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 if for (i = 0; i < n; i++){ tempBuf[i] = toupper(tempBuf[i]); }//of for i write(tempAcceptFd, tempBuf, tempDataLen); } close(tempAcceptFd); close(tempListenFd); return 0; }//of main



