UNIX环境高级编程学习之第十六章网络IPC:套接字 - 非阻塞的Socket通信EPoll模型(多路复用), 实用Socket通信模板

来源:互联网 发布:光触媒能除甲醛吗 知乎 编辑:程序博客网 时间:2024/05/21 10:11

UNIX环境高级编程学习之第十六章网络IPC:套接字 - 非阻塞的Socket通信EPoll模型(多路复用), 实用Socket通信模板

 

/* User:Lixiujie         * Date:20101207* Desc:Unix(Linux)非阻塞的Socket通信EPoll模型,多路复用,TCP服务器端, 向客户端发送响应信息。* File:tcp_server_epoll.c  * System:Ubuntu 64bit  * gcc tcp_server_epoll.c -o  tcp_server_epoll* tcp_server_epoll 7878*EPoll 函数介绍epoll是Linux内核为处理大批量句柄而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。EPoll 优点1、它保留了poll的两个相对与select的优点2、epoll_wait的参数events作为出参,直接返回了有事件发生的fd,epoll_wait的返回值既是发生事件的个数,省略了poll中返回之后的循环操作。3、不再象select、poll一样将标识符局限于fd,epoll中可以将标识符扩大为指针,大大增加了epoll模型下的灵活性。EPoll 使用说明事项1、如果fd被注册到两个epoll中时,如果有时间发生则两个epoll都会触发事件。2、如果注册到epoll中的fd被关闭,则其会自动被清除出epoll监听列表。注意:关闭自动清除,不用手机清除3、如果多个事件同时触发epoll,则多个事件会被联合在一起返回。4、epoll_wait会一直监听epollhup事件发生,所以其不需要添加到events中。5、为了避免大数据量io时,et模式下只处理一个fd,其他fd被饿死的情况发生。linux建议可以在fd联系到的结构中增加ready位,然后epoll_wait触发事件之后仅将其置位为ready模式,然后在下边轮询ready fd列表。6、epoll_ctl epoll的事件注册函数,其events参数可以是以下宏的集合:        EPOLLIN:    表示对应的文件描述符可以读(包括对端SOCKET正常关闭);        EPOLLOUT:   表示对应的文件描述符可以写;        EPOLLPRI:   表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);        EPOLLERR:   表示对应的文件描述符发生错误;写已关闭socket pipe broken         EPOLLHUP:   表示对应的文件描述符被挂断;譬如收到RST包。在注册事件的时候这个事件是默认添加。        EPOLLRDHUP: 表示对应的文件描述符对端socket关闭事件,主要应用于ET模式下。        在水平触发模式下,如果对端socket关闭,则会一直触发epollin事件,驱动去处理client socket。        在边沿触发模式下,如果client首先发送协议然后shutdown写端。则会触发epollin事件。但是如果处理程序只进行一次recv操作时,根据recv收取到得数据长度来判读后边是否还有需要处理的协议时,将丢失客户端关闭事件。        EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。        EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里EPoll 工作模式1、水平触发Level Triggered (LT) 是EPoll的缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你 的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.2、边缘触发Edge  Triggered (ET) 是EPoll的高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述 符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致 了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认。内核的读buffer有内核态主动变化时,内核会通知你, 无需再去mod。写事件是给用户使用的,最开始add之后,内核都不会通知你了,你可以强制写数据(直到EAGAIN或者实际字节数小于 需要写的字节数),当然你可以主动mod OUT,此时如果句柄可以写了(send buffer有空间),内核就通知你。 这里内核态主动的意思是:内核从网络接收了数据放入了读buffer(会通知用户IN事件,即用户可以recv数据) 并且这种通知只会通知一次,如果这次处理(recv)没有到刚才说的两种情况(EAGIN或者实际字节数小于 需要读写的字节数),则该事件会被丢弃,直到下次buffer发生变化。 与LT的差别就在这里体现,LT在这种情况下,事件不会丢弃,而是只要读buffer里面有数据可以让用户读,则不断的通知你。EPoll 函数使用介绍1、EPoll 创建epoll句柄函数。int epoll_create(int size);        参数size:用来告诉内核要监听的数目一共有多少个。        返回值:成功时,返回一个非负整数的文件描述符,作为创建好的epoll句柄。调用失败时,返回-1,错误信息可以通过errno获得。    说明:创建一个epoll句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。2、EPoll 注册修改删除文件描述符到epoll句柄函数int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);    参数epfd:epoll_create()函数返回的epoll句柄。        参数op:操作选项。        参数fd:要进行操作的目标文件描述符。        参数event:struct epoll_event结构指针,将fd和要进行的操作关联起来。        返回值:成功时,返回0,作为创建好的epoll句柄。调用失败时,返回-1,错误信息可以通过errno获得。        说明:epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。        参数op的可选值有以下3个:        EPOLL_CTL_ADD:注册新的fd到epfd中;        EPOLL_CTL_MOD:修改已经注册的fd的监听事件;        EPOLL_CTL_DEL:从epfd中删除一个fd;        events可以是以下几个宏的集合:        EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);        EPOLLOUT:表示对应的文件描述符可以写;        EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);        EPOLLERR:表示对应的文件描述符发生错误;        EPOLLHUP:表示对应的文件描述符被挂断;        EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。        EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里3、EPoll 等待事件的产生函数int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);        参数epfd:epoll_create()函数返回的epoll句柄。        参数events:struct epoll_event结构指针,用来从内核得到事件的集合。        参数 maxevents:告诉内核这个events有多大        参数 timeout: 等待时的超时时间,以毫秒为单位。        返回值:成功时,返回需要处理的事件数目。调用失败时,返回0,表示等待超时。        说明:等待事件的产生。        timeout值 说明         -1 永远等待         0 立即返回,不阻塞进程         >0 等待指定数目的毫秒数 EPoll 注意事项        建立连接的时候epoll_add IN和OUT事件, 后面就不需要管了         每次read/write的时候,到两种情况下结束:         1 发生EAGAIN         2 read/write的实际字节数小于 需要读写的字节数         对于第二点需要注意两点:         A:如果是UDP服务,处理就不完全是这样,必须要recv到发生EAGAIN为止,否则就丢失事件了         因为UDP和TCP不同,是有边界的,每次接收一定是一个完整的UDP包,当然recv的buffer需要至少大于一个UDP包的大小         随便再说一下,一个UDP包到底应该多大?         对于internet,由于MTU的限制,UDP包的大小不要超过576个字节,否则容易被分包,对于公司的IDC环境,建议不要超过1472,否则也比较容易分包。         B 如果发送方发送完数据以后,就close连接,这个时候如果recv到数据是实际字节数小于读写字节数,根据开始所述就认为到EAGIN了从而直接返回,等待下一次事件,这样是有问题的,close事件丢失了!         因此如果依赖这种关闭逻辑的服务,必须接收数据到EAGIN为止。*/#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 <sys/epoll.h>#include <unistd.h>#include <fcntl.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;}/* 设置为非阻塞模式 */void setnonblocking(int sock){       int opts;       opts=fcntl(sock,F_GETFL);       if(opts<0){           perror("fcntl(sock,GETFL)");           exit(1);       }       opts = opts|O_NONBLOCK;       if(fcntl(sock,F_SETFL,opts)<0){           perror("fcntl(sock,SETFL,opts)");           exit(1);       }   }/* EPoll ET模式下读取数据函数, 保证 szBuf空间足够大, 否则数据丢失 */int epoolRecv(int fd, char *szBuf, int nBuflen){        int recvTotalLen = 0, recvLen = 0;        char szTmp[1024] = { 0x00 };        while (1) {                memset(szTmp, 0x00, sizeof(szTmp));                recvLen = recv(fd, szTmp, sizeof(szTmp), 0);                if (recvLen < 0){                        if (EAGAIN == errno) {                                return recvTotalLen; /* 读取结束,正常返回 */                        } else {                                perror("Err:epoolRecv recv err!");                                return -1;                        }                }else if (0 == recvLen) {                        return 0; /* 对方Socket正常关闭 */                }                if (recvTotalLen + recvLen <= nBuflen){                        memcpy(szBuf + recvTotalLen, szTmp, recvLen);                }                recvTotalLen += recvLen;                if (recvLen < sizeof(szTmp)){ /* TCP 可以用这种模式, UDP 可能报EAGAIN才能结束 */                        return recvTotalLen; /* 读取结束,正常返回 */                }        }        return recvTotalLen;}/* EPoll ET模式下发送数据函数 */int epollSend(int fd, const char *szBuf, int nBuflen){        int sendTotalLen = 0, sendLen = 0;        char szTmp[1024] = { 0x00 };        while (1) {                sendLen = send(fd, szBuf + sendTotalLen, nBuflen - sendTotalLen, 0);                if (sendLen < 0) {                        /* 当socket是非阻塞时,如返回此错误,表示写缓冲队列已满,在这里做延时后再重试. */                        if (errno == EAGAIN) {                                usleep(1000);                                continue;                        }                        return -1;                }else if (0 == sendLen) {                        return 0;                }                sendTotalLen += sendLen;                if(sendTotalLen == nBuflen)                        return sendTotalLen;        }        return sendTotalLen;} 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);        int epfd = epoll_create(256);/* 创建一个epoll句柄,size用来告诉内核这个监听的数目一共有多大 */        struct epoll_event ev, events[QLEN];/* 声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件 */        /* 注册Server socket事件到EPoll */        ev.data.fd = sefd;        ev.events = EPOLLIN|EPOLLET; /* 读事件、ET模式  */        epoll_ctl(epfd, EPOLL_CTL_ADD, sefd, &ev); /* 注册epoll事件 */        int i, nevs;        while (1){                printf("epoll_wait before OK !\n");                nevs = epoll_wait(epfd, events, QLEN, -1);                 printf("epoll_wait after OK !ret = %d\n", ret);                if (nevs < 0){if(errno == EINTR && epfd > 0){usleep(10*1000);continue;}printf("epoll_wait err=%s!\n", strerror(errno));while (head != NULL){node = head;head = head->pNext;close(node->sock);free(node);}return -1;                }else if (0 == nevs) { /* 不可能出现  */                        printf("epoll_wait timeout!\n");                        sleep(1);                        continue;                }                for (i = 0; i < nevs; i++){                                if (events[i].data.fd == sefd){ /* Server Socket */                                alen = sizeof(struct sockaddr);                                memset((void *)&cl_addr, 0 , alen);                                clfd = accept(events[i].data.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));                                setnonblocking(clfd); /* 设置客户端为非阻塞模式 */                                ev.data.fd = clfd;                                ev.events = EPOLLIN|EPOLLET; /* 读事件、ET模式  */                                epoll_ctl(epfd, EPOLL_CTL_ADD, clfd, &ev); /* 注册epoll事件 */                        }else if (events[i].events | EPOLLIN){                                node = selectSockNodeList(head, events[i].data.fd);                                if (NULL == node){                                        continue;                                }                                memset(szBuf, 0, BUFLEN);                                /* ret = recv(node->sock, szBuf, BUFLEN, 0); */                                ret = epoolRecv(node->sock, szBuf, BUFLEN);                                if (ret < 0){                                        printf("epoolRecv Client err=%s, ip=%s, port=%d!\n", strerror(errno),                                                 inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));                                        close(node->sock);                                        events[i].data.fd = -1;                                           head =  deleteSockNodeList(head, node->sock);                                } else if (0 == ret){                                        printf("epoolRecv Client exit, ip=%s, port=%d!\n",                                                 inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));                                        close(node->sock);                                        events[i].data.fd = -1;                                        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("epoolRecv 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);                                                        epollSend(node->sock, szBuf, sizeof(MyMsg) + len);                                                        printf("epollSend 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("epoolRecv 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("epoolRecv 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);                                                        epollSend(node->sock, szBuf, sizeof(MyMsg) + len);                                                        printf("epollSend 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);                                                events[i].data.fd = -1;                                                head = deleteSockNodeList(head, node->sock);                                        }else if (strcmp(msgRecv->szCmd, "RESP_TEXT") == 0){                                                printf("epoolRecv Client RESP_TEXT, ip=%s, port=%d!\n",                                                         inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));                                                close(node->sock);                                                events[i].data.fd = -1;                                                head = deleteSockNodeList(head, node->sock);                                        }else if (strcmp(msgRecv->szCmd, "RE_DATA") == 0){                                                printf("epoolRecv 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);                                                        epollSend(node->sock, szBuf, sizeof(MyMsg) + len);                                                        printf("epollSend 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("epoolRecv 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:20101207* Desc:Unix(Linux)非阻塞的Socket通信EPoll模型,多路复用,TCP客户端, 向服务端发送请求信息,接收响应信息。* 可以在发送ls uptime pwd 等简单的显示命令* File:tcp_client_epoll.c  * System:Ubuntu 64bit  * gcc tcp_client_epoll.c -o  tcp_client_epoll* tcp_client_epoll 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;}


 


原创粉丝点击