- /* 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;
- }