UNIX环境高级编程学习之第十六章网络IPC:套接字 - 非阻塞的Socket通信Poll模型(多路复用), 实用Socket通信模板 /* User:Lixiujie * Date:20101206 * Desc:Unix(Linux)非阻塞的Socket通信Poll模型,多路复用,TCP服务器端, 向客户端发送响应信息。 * File:tcp_server_poll.c * System:Ubuntu 64bit * gcc tcp_server_poll.c -o tcp_server_poll * tcp_server_poll 7878 * * poll函数可用的测试值 POLLIN 普通或优先级带数据可读 POLLRDNORM 普通数据可读 POLLRDBAND 优先级带数据可读 POLLPRI 高优先级数据可读 POLLOUT 普通数据可写 POLLWRNORM 普通数据可写 POLLWRBAND 优先级带数据可写 POLLERR 发生错误 POLLHUP 发生挂起 POLLNVAL 描述字不是一个打开的文件 timeout值 说明 -1 永远等待 0 立即返回,不阻塞进程 >0 等待指定数目的毫秒数 */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> /* socket bind listen connect accept send recv */ #include <arpa/inet.h> /* htons ntohs htonl ntohl inet_addr inet_ntoa */ #include <netinet/in.h> /* sockaddr_in */ #include <poll.h> #define BUFLEN 1024 #define QLEN 20 /* 传送信息结构体 */ typedef struct _MyMsg{ char szCmd[16];/* message command * RE_LINK:test link request * RESP_LINK:test link response * RE_EXIT: exit request * RESP_TEXT: exit response * RE_DATA: data request * RESP_DATA: data response */ int iLen; /* message data length*/ char szData[0];/* message data */ }MyMsg; /* 信息处理 */ MyMsg * MsgProcess(MyMsg *pMsgIn){ char szBuf[BUFLEN] = { 0x00 }; char szTmp[BUFLEN] = { 0x00 }; MyMsg *pMsg = NULL; FILE *fp; if (strcmp(pMsgIn->szCmd, "RE_LINK") == 0){ pMsg = (MyMsg *)malloc(sizeof(MyMsg)); memset(pMsg, 0, sizeof(MyMsg)); strcpy(pMsg->szCmd, "RESP_LINK"); }else if (strcmp(pMsgIn->szCmd, "RESP_LINK") == 0){ }else if (strcmp(pMsgIn->szCmd, "RE_EXIT") == 0){ pMsg = (MyMsg *)malloc(sizeof(MyMsg)); memset(pMsg, 0, sizeof(MyMsg)); strcpy(pMsg->szCmd, "RESP_TEXT"); }else if (strcmp(pMsgIn->szCmd, "RESP_TEXT") == 0){ }else if (strcmp(pMsgIn->szCmd, "RE_DATA") == 0){ memset(szBuf, 0, BUFLEN); strncpy(szBuf, pMsgIn->szData, pMsgIn->iLen); if ((fp = popen(szBuf, "r")) == NULL){ memset(szBuf, 0, BUFLEN); sprintf(szBuf, "error: %s/n", strerror(errno)); }else{ memset(szTmp, 0, BUFLEN); while (fgets(szTmp, BUFLEN, fp) != NULL){ strcat(szBuf, szTmp); memset(szTmp, 0, BUFLEN); } pclose(fp); } pMsg = (MyMsg *)malloc(sizeof(MyMsg)+ strlen(szBuf)+1); memset(pMsg, 0, sizeof(MyMsg)); strcpy(pMsg->szCmd, "RESP_DATA"); pMsg->iLen = strlen(szBuf)+1; strcpy(pMsg->szData, szBuf); }else if (strcmp(pMsgIn->szCmd, "RESP_DATA") == 0){ } return pMsg; } /* Socket结构体 */ typedef struct _SockNode{ int sock; struct sockaddr_in addr; struct _SockNode *pNext; }SockNode; /* create SockNode */ SockNode* createSockNode(int sock, struct sockaddr_in *pAddr){ SockNode *p = NULL; if ((p = (SockNode *)malloc(sizeof(SockNode))) != NULL){ p->sock = sock; memcpy(&(p->addr), pAddr, sizeof(struct sockaddr_in)); p->pNext = NULL; } return p; } /* add SockNode from list */ void addSockNodeList(SockNode *head, SockNode *node){ SockNode *p = head; while (p->pNext != NULL){ p = p->pNext; } p->pNext = node; } /* delete SockNode from list * return head */ SockNode* deleteSockNodeList(SockNode *head, int sock){ SockNode *p = head, *pPrevious = p; while (p != NULL){ if (p->sock == sock){ if (p != pPrevious){ pPrevious->pNext = p->pNext; }else{ head = p->pNext; } free(p); break; } pPrevious = p; p = p->pNext; } return head; } /* select SockNode from list * return head */ SockNode* selectSockNodeList(SockNode *head, int sock){ SockNode *p = head, *pPrevious = p; while (p != NULL){ if (p->sock == sock){ return p; } p = p->pNext; } return NULL; } /* maximumly sock from list */ int maxSockNodeList(SockNode *head){ SockNode *p = head; int maxsock = -1; while (p != NULL){ maxsock = maxsock > p->sock ? maxsock : p->sock; p = p->pNext; } return maxsock; } /* create tcp server */ int initserver(int type, const struct sockaddr *addr, socklen_t alen, int qlen){ int fd; int err = 0, iSockAttrOn = 1; /* 创建 */ if ((fd = socket(addr->sa_family, type, 0)) < 0){ return -1; } /* 端口复用 */ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &iSockAttrOn, sizeof(iSockAttrOn) ) < 0){ err = errno; goto errout; } /* 绑定 */ if (bind(fd, addr, alen) < 0){ err = errno; goto errout; } /* 监听数 */ if (SOCK_STREAM == type || SOCK_SEQPACKET == type){ if (listen(fd, qlen) < 0) { err = errno; goto errout; } } return fd; errout: close(fd); errno = err; return -1; } int main(int argc, char *argv[]){ if (argc != 2){ printf("arg err!/n"); return -1; } int sefd, clfd, ret, len; char szBuf[BUFLEN]; SockNode *head = NULL,*node = NULL; /* socket 监听链表 */ struct sockaddr_in se_addr,cl_addr; socklen_t alen = sizeof(struct sockaddr); /* 设置服务IP和端口 */ memset((void *)&se_addr, 0, alen); se_addr.sin_family = AF_INET; se_addr.sin_addr.s_addr = INADDR_ANY;// inet_addr("0.0.0.0"); se_addr.sin_port = htons(atoi(argv[1])); if ((sefd = initserver(SOCK_STREAM, (struct sockaddr *)&se_addr, alen, QLEN)) < 0){ printf("initserver err=%s!/n", strerror(errno)); return -1; } printf("initserver OK !/n"); head = createSockNode(sefd, &se_addr); struct pollfd fds[QLEN];/* 用于poll函数第一个参数的数组 */ int nfds = 0; int i; while (1){ node = head; nfds = 0; while(node != NULL){ fds[nfds].fd = node->sock; fds[nfds].events = POLLIN | POLLPRI; /* 普通或优先级或高优先级带数据可读 */ ++nfds; node = node->pNext; } for (i = nfds;i < QLEN; i++){ fds[i].fd = -1; /* 数组中的其它元素将暂时设置成不可用 */ } printf("poll before OK !nfds=%d/n", nfds); ret = poll(fds, nfds, -1); printf("poll after OK !ret = %d/n", ret); if (ret < 0){ printf("poll err=%s!/n", strerror(errno)); while (head != NULL){ node = head; head = head->pNext; close(node->sock); free(node); } return -1; }else if (0 == ret) { /* 不可能出现 */ printf("poll timeout!/n"); sleep(1); continue; } for (i = 0; i < nfds; i++){ if (0 == i && ((fds[i].revents & POLLIN) || (fds[i].revents & POLLPRI))){ alen = sizeof(struct sockaddr); memset((void *)&cl_addr, 0 , alen); clfd = accept(fds[i].fd, (struct sockaddr*)&cl_addr, &alen); if (clfd < 0){ printf("accept err=%s!/n", strerror(errno)); while (head != NULL){ node = head; head = head->pNext; close(node->sock); free(node); } return -1; } printf("Client connect:ip=%s, port=%d /n", inet_ntoa(cl_addr.sin_addr), ntohs(cl_addr.sin_port)); addSockNodeList(head, createSockNode(clfd, &cl_addr)); }else if ((fds[i].revents & POLLIN) || (fds[i].revents & POLLPRI)){ node = selectSockNodeList(head, fds[i].fd); if (NULL == node){ continue; } memset(szBuf, 0, BUFLEN); ret = recv(node->sock, szBuf, BUFLEN, 0); if (ret < 0){ printf("recv Client err=%s, ip=%s, port=%d!/n", strerror(errno), inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port)); close(node->sock); head = deleteSockNodeList(head, node->sock); } else if (0 == ret){ printf("recv Client exit, ip=%s, port=%d!/n", inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port)); close(node->sock); head = deleteSockNodeList(head, node->sock); } else { MyMsg *msgRecv = (MyMsg *)szBuf; msgRecv->iLen = ntohl(msgRecv->iLen);/* 转换成本机字节序 */ MyMsg *msgSend = NULL; /* 预处理 */ if (strcmp(msgRecv->szCmd, "RE_LINK") == 0){ printf("recv Client RE_LINK, ip=%s, port=%d!/n", inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port)); msgSend = MsgProcess(msgRecv); /* 实际处理 */ if (msgSend != NULL){ len = msgSend->iLen; msgSend->iLen = htonl(msgSend->iLen); /* 转换成网络字节序 */ memset(szBuf, 0, BUFLEN); memcpy(szBuf, msgSend, sizeof(MyMsg) + len); send(node->sock, szBuf, sizeof(MyMsg) + len, 0); printf("send Client %s, ip=%s, port=%d!/n", msgSend->szCmd, inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port)); free(msgSend); msgSend = NULL; } }else if (strcmp(msgRecv->szCmd, "RESP_LINK") == 0){ printf("recv Client RESP_LINK, ip=%s, port=%d!/n", inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port)); }else if (strcmp(msgRecv->szCmd, "RE_EXIT") == 0){ printf("recv Client RE_EXIT, ip=%s, port=%d!/n", inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port)); msgSend = MsgProcess(msgRecv); /* 实际处理 */ if (msgSend != NULL){ len = msgSend->iLen; msgSend->iLen = htonl(msgSend->iLen); /* 转换成网络字节序 */ memset(szBuf, 0, BUFLEN); memcpy(szBuf, msgSend, sizeof(MyMsg) + len); send(node->sock, szBuf, sizeof(MyMsg) + len, 0); printf("send Client %s, ip=%s, port=%d!/n", msgSend->szCmd, inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port)); free(msgSend); msgSend = NULL; } close(node->sock); head = deleteSockNodeList(head, node->sock); }else if (strcmp(msgRecv->szCmd, "RESP_TEXT") == 0){ printf("recv Client RESP_TEXT, ip=%s, port=%d!/n", inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port)); close(node->sock); head = deleteSockNodeList(head, node->sock); }else if (strcmp(msgRecv->szCmd, "RE_DATA") == 0){ printf("recv Client RE_DATA, ip=%s, port=%d, data:%s!/n", inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port), msgRecv->szData); msgSend = MsgProcess(msgRecv); /* 实际处理 */ if (msgSend != NULL){ len = msgSend->iLen; msgSend->iLen = htonl(msgSend->iLen); /* 转换成网络字节序 */ memset(szBuf, 0, BUFLEN); memcpy(szBuf, msgSend, sizeof(MyMsg) + len); send(node->sock, szBuf, sizeof(MyMsg) + len, 0); printf("send Client %s, ip=%s, port=%d, data:%s!/n", msgSend->szCmd, inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port), len > 0 ? msgSend->szData : ""); free(msgSend); msgSend = NULL; } }else if (strcmp(msgRecv->szCmd, "RESP_DATA") == 0){ printf("recv Client RESP_DATA, ip=%s, port=%d, data:%s!/n", inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port), msgRecv->szData); } }/* recv */ }/* if i = 0 */ }/*for */ }/* while 1 */ while (head != NULL){ node = head; head = head->pNext; close(node->sock); free(node); } return 0; } /* User:Lixiujie * Date:20101123 * Desc:Unix(Linux)非阻塞Socket通信Poll模型,多路复用,TCP客户端, 向服务端发送请求信息,接收响应信息。 * 可以在发送ls uptime pwd 等简单的显示命令 * File:tcp_client_poll.c * System:Ubuntu 64bit * gcc tcp_client_poll.c -o tcp_client_poll * tcp_client_poll 127.0.0.1 7878 */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> /* socket bind listen connect accept send recv */ #include <arpa/inet.h> /* htons ntohs htonl ntohl inet_addr inet_ntoa */ #include <netinet/in.h> /* sockaddr_in */ #include <pthread.h> /* multithreading */ #define BUFLEN 1024 /* 传送信息结构体 */ typedef struct _MyMsg{ char szCmd[16];/* message command * RE_LINK:test link request * RESP_LINK:test link response * RE_EXIT: exit request * RESP_TEXT: exit response * RE_DATA: data request * RESP_DATA: data response */ int iLen; /* message data length*/ char szData[0];/* message data */ }MyMsg; /* 信息处理 */ MyMsg * MsgProcess(MyMsg *pMsgIn){ char szBuf[BUFLEN] = { 0x00 }; char szTmp[BUFLEN] = { 0x00 }; MyMsg *pMsg = NULL; FILE *fp; if (strcmp(pMsgIn->szCmd, "RE_LINK") == 0){ pMsg = (MyMsg *)malloc(sizeof(MyMsg)); memset(pMsg, 0, sizeof(MyMsg)); strcpy(pMsg->szCmd, "RESP_LINK"); }else if (strcmp(pMsgIn->szCmd, "RESP_LINK") == 0){ }else if (strcmp(pMsgIn->szCmd, "RE_EXIT") == 0){ pMsg = (MyMsg *)malloc(sizeof(MyMsg)); memset(pMsg, 0, sizeof(MyMsg)); strcpy(pMsg->szCmd, "RESP_TEXT"); }else if (strcmp(pMsgIn->szCmd, "RESP_TEXT") == 0){ }else if (strcmp(pMsgIn->szCmd, "RE_DATA") == 0){ memset(szBuf, 0, BUFLEN); strncpy(szBuf, pMsgIn->szData, pMsgIn->iLen); if ((fp = popen(szBuf, "r")) == NULL){ memset(szBuf, 0, BUFLEN); sprintf(szBuf, "error: %s/n", strerror(errno)); }else{ memset(szTmp, 0, BUFLEN); while (fgets(szTmp, BUFLEN, fp) != NULL){ strcat(szBuf, szTmp); memset(szTmp, 0, BUFLEN); } pclose(fp); } pMsg = (MyMsg *)malloc(sizeof(MyMsg)+ strlen(szBuf)+1); memset(pMsg, 0, sizeof(MyMsg)); strcpy(pMsg->szCmd, "RESP_DATA"); pMsg->iLen = strlen(szBuf)+1; strcpy(pMsg->szData, szBuf); }else if (strcmp(pMsgIn->szCmd, "RESP_DATA") == 0){ } return pMsg; } int recvProcess(int sefd){ fd_set rdset; struct timeval timeout = {5, 0}; char szBuf[BUFLEN] = { 0x00 }; int ret, len; FD_ZERO(&rdset); FD_SET(sefd, &rdset); ret = select(sefd + 1, &rdset, NULL, NULL, &timeout); if (ret < 0){ printf("select err:%s/n", strerror(errno)); exit(-1); }else if (0 == ret){ memset(szBuf, 0, BUFLEN); strcpy(szBuf, "RE_LINK"); send(sefd, szBuf, strlen(szBuf) + sizeof(MyMsg), 0); printf("select timeout send: RE_LINK/n"); recvProcess(sefd); return -1; }else{ memset(szBuf, 0, BUFLEN); ret = recv(sefd, szBuf, BUFLEN, 0); if (ret < 0){ printf("recv err:%s/n", strerror(errno)); close(sefd); exit(-1); } else if (0 == ret){ printf("recv server close!/n"); close(sefd); exit(-1); } else { MyMsg *msgRecv = (MyMsg *)szBuf; msgRecv->iLen = ntohl(msgRecv->iLen);/* 转换成本机字节序 */ MyMsg *msgSend = NULL; /* 预处理 */ if (strcmp(msgRecv->szCmd, "RE_LINK") == 0){ printf("recv Server RE_LINK!/n"); msgSend = MsgProcess(msgRecv); /* 实际处理 */ if (msgSend != NULL){ len = msgSend->iLen; msgSend->iLen = htonl(msgSend->iLen); /* 转换成网络字节序 */ memset(szBuf, 0, BUFLEN); memcpy(szBuf, msgSend, sizeof(MyMsg) + len); send(sefd, szBuf, sizeof(MyMsg) + len, 0); printf("send Server %s,/n", msgSend->szCmd); free(msgSend); msgSend = NULL; } }else if (strcmp(msgRecv->szCmd, "RESP_LINK") == 0){ printf("recv Server RESP_LINK!/n"); }else if (strcmp(msgRecv->szCmd, "RE_EXIT") == 0){ printf("recv Server RE_EXIT!/n"); msgSend = MsgProcess(msgRecv); /* 实际处理 */ if (msgSend != NULL){ len = msgSend->iLen; msgSend->iLen = htonl(msgSend->iLen); /* 转换成网络字节序 */ memset(szBuf, 0, BUFLEN); memcpy(szBuf, msgSend, sizeof(MyMsg) + len); send(sefd, szBuf, sizeof(MyMsg) + len, 0); printf("send Server %s!/n", msgSend->szCmd); free(msgSend); msgSend = NULL; } close(sefd); exit(0); }else if (strcmp(msgRecv->szCmd, "RESP_TEXT") == 0){ printf("recv Server RESP_TEXT!/n"); close(sefd); exit(0); }else if (strcmp(msgRecv->szCmd, "RE_DATA") == 0){ printf("recv Server RE_DATA, data:%s!/n", msgRecv->szData); msgSend = MsgProcess(msgRecv); /* 实际处理 */ if (msgSend != NULL){ len = msgSend->iLen; msgSend->iLen = htonl(msgSend->iLen); /* 转换成网络字节序 */ memset(szBuf, 0, BUFLEN); memcpy(szBuf, msgSend, sizeof(MyMsg) + len); send(sefd, szBuf, sizeof(MyMsg) + len, 0); printf("send Server %s, data:%s!/n", msgSend->szCmd, len > 0 ? msgSend->szData : ""); free(msgSend); msgSend = NULL; } }else if (strcmp(msgRecv->szCmd, "RESP_DATA") == 0){ printf("recv Server RESP_DATA, data:%s!/n", msgRecv->szData); } } } return 0; } int main(int argc, char *argv[]){ if (argc != 3){ printf("arg err!/n"); return -1; } int sefd, ret, len; char szBuf[BUFLEN]; struct sockaddr_in se_addr, my_addr; MyMsg *pMsg; socklen_t alen = sizeof(struct sockaddr); /* 设置服务端的IP和端口 */ memset((void *)&se_addr, 0, alen); se_addr.sin_family = AF_INET; se_addr.sin_addr.s_addr = inet_addr(argv[1]); se_addr.sin_port = htons(atoi(argv[2])); if ((sefd = socket(se_addr.sin_family, SOCK_STREAM, 0)) < 0){ printf("socket err:%s/n", strerror(errno)); return -1; } if (connect(sefd, (struct sockaddr *)&se_addr, alen) < 0){ printf("connect err:%s/n", strerror(errno)); return -1; } alen = sizeof(struct sockaddr); memset((void *)&my_addr, 0, alen); getsockname(sefd, (struct sockaddr *)&my_addr, &alen); printf("connect OK, 本机IP:%s, Port:%d/n", inet_ntoa(my_addr.sin_addr), ntohs(my_addr.sin_port)); while (1){ memset(szBuf, 0, BUFLEN); printf("Input data:"); gets(szBuf); if (strncmp(szBuf, "link", 4) == 0){ memset(szBuf, 0, BUFLEN); strcpy(szBuf, "RE_LINK"); send(sefd, szBuf, strlen(szBuf) + sizeof(MyMsg), 0); printf("Send Server CMD: RE_LINK/n"); recvProcess(sefd); }else if (strncmp(szBuf, "exit", 4) == 0){ memset(szBuf, 0, BUFLEN); strcpy(szBuf, "RE_EXIT"); send(sefd, szBuf, strlen(szBuf) + sizeof(MyMsg), 0); printf("Send Server CMD: RE_EXIT/n"); recvProcess(sefd); }else if (strlen(szBuf) > 0){ pMsg = (MyMsg *)malloc(sizeof(MyMsg) + strlen(szBuf)+1); strcpy(pMsg->szCmd, "RE_DATA"); pMsg->iLen = strlen(szBuf)+1; strcpy(pMsg->szData, szBuf); len = pMsg->iLen; pMsg->iLen = htonl(pMsg->iLen); /* 转换成网络字节序 */ memset(szBuf, 0, BUFLEN); memcpy(szBuf, pMsg, sizeof(MyMsg) + len); send(sefd, szBuf, sizeof(MyMsg) + len, 0); printf("Send Server CMD: RE_DATA, data:%s/n", pMsg->szData); free(pMsg); recvProcess(sefd); }else{ printf("Error: Input err!/n"); } } return 0; }