epoll实现TCP通信
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了epoll实现TCP通信相关的知识,希望对你有一定的参考价值。
epoll是linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
注:epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
注:epoll文件描述符用完后,直接用close关闭即可,非常方便。事实上,任何被侦听的文件符只要其被关闭,那么它也会自动从被侦听的文件描述符集合中删除,很是智能。
epoll相关的系统调用有:epoll_create, epoll_ctl和epoll_wait。
1.epoll_create用来创建一个epoll文件描述符。
返回值:
>0:非空文件描述符;
-1:函数调用失败,同时会自动设置全局变量errno;
2.epoll_ctl用来添加/修改/删除需要侦听的文件描述符及其事件.
epoll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。
epfd:epoll_create的返回值
op:表示动作,为以下三个宏任意一个(根据需要):添加,修改,删除
fd:为关心的描述符
event为:关心描述符事件
3.epoll_wait/epoll_pwait接收发生在被侦听的描述符上的,用户感兴趣的IO事件。
收集在epoll监控的事件中已经就绪的事件。
events:是分配好的epoll_event结构体数组,epoll将会把就绪的事件赋值到events数组中(events不可以是空指针,内核只负责把数据复制到这个events数组中)。
maxevents:告之内核这个events有多大,这个 maxevents的值不能小于创建epoll_create()时的size
timeout:是超时时间(毫秒)
1.0表示轮询非阻塞,立即返回;
2.-1将不确定,也有说法说是永久阻塞。
3.大于0,以timeput事件轮询返回
返回值:
1.成功,返回对应I/O上已准备好的文件描述符数
2.0表示已超时。
3.-1,发生错误。
#include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<sys/socket.h> #include<sys/types.h> #include<netinet/in.h> #include<arpa/inet.h> #include<sys/epoll.h> #define _SIZE_ 64 #define _BACKLOG_ 5 typedef struct fdBuf { void * _buf; int _fd; }fdBuf; static void usage(const char* proc) { printf("%s [ip][port]",proc); } static int startup(char* ip,int port) { int listen_sock=socket(AF_INET,SOCK_STREAM,0); if(listen_sock<0) { perror("socket"); exit(1); } struct sockaddr_in local; local.sin_family=AF_INET; local.sin_port=htons(port); local.sin_addr.s_addr=inet_addr(ip); if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local))<0) { perror("bind"); exit(2); } if(listen(listen_sock,_BACKLOG_)<0) { perror("listen"); exit(3); } return listen_sock; } int sock_epoll(int listen_sock) { //1.create fds instance int ins=epoll_create(_SIZE_); if(ins<0) { perror("poll_create"); return 1; } struct epoll_event ev; ev.events=EPOLLIN; ev.data.fd=listen_sock; int i=0;//index fdBuf bufs[_SIZE_]; for(i=0;i<_SIZE_;++i) { bufs[i]._fd=-1; bufs[i]._buf=NULL; } struct epoll_event fds[_SIZE_];//with bufs save buf for(i=0;i<_SIZE_;++i) { fds[i].events=0; fds[i].data.fd=-1; } epoll_ctl(ins,EPOLL_CTL_ADD,listen_sock,&ev); int ret=-1; int timeout=5000; struct sockaddr_in remote; socklen_t len=sizeof(remote); ssize_t _s;//charnum while(1) { switch((ret=epoll_wait(ins,fds,64,timeout))) { case -1://error perror("epoll_wait"); break; case 0://time out printf("time is out\n"); break; default: { for(i=0;i<ret;++i) { //printf("%d",ret); if(fds[i].data.fd==listen_sock) { if(new_sock<0) { perror("accept"); continue; } ev.events=EPOLLIN; ev.data.fd=new_sock; epoll_ctl(ins,EPOLL_CTL_ADD,new_sock,&ev); } else if(fds[i].data.fd>0&&fds[i].events&EPOLLIN) { if(bufs[i]._fd==-1) { char *buf=(char*)malloc(sizeof(char)*1024); bufs[i]._fd=fds[i].data.fd; bufs[i]._buf=buf; } //save buf and fd memset(bufs[i]._buf,‘\0‘,1024); //sleep(1); fflush(stdout); _s=read(fds[i].data.fd,bufs[i]._buf,sizeof(bufs[i]._buf)-1); if(_s>0) { ((char*)bufs[i]._buf)[_s]=‘\0‘; printf("client:%s",(char*)bufs[i]._buf);//输出 ev.events=EPOLLOUT; ev.data.fd=fds[i].data.fd; epoll_ctl(ins,EPOLL_CTL_MOD,fds[i].data.fd,&ev); } else if(_s==0) { printf("client is close...\n"); free(bufs[i]._buf); bufs[i]._fd=-1; bufs[i]._buf=NULL; //remove epoll_ctl(ins,EPOLL_CTL_DEL,fds[i].data.fd,NULL); } else {} } else if(fds[i].data.fd>0&&fds[i].events&EPOLLOUT) { write(fds[i].data.fd,bufs[i]._buf,strlen(bufs[i]._buf)); ev.events=EPOLLIN; ev.data.fd=fds[i].data.fd; epoll_ctl(ins,EPOLL_CTL_MOD,fds[i].data.fd,&ev); } else {} } break; }//default end }//switch end }//while end } int main(int argc,char* argv[]) { if(argc!=3) { usage(argv[0]); return 1; } int _port=atoi(argv[2]); char* _ip=argv[1]; int listen_sock=startup(_ip,_port); sock_epoll(listen_sock); close(listen_sock); return 0; } //client #include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> static void usage(const char* proc) { printf("%s [i][port]",proc); } int main(int argc,char* argv[]) { if(argc!=3) { usage(argv[0]); return 1; } int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("socket"); return 2; } struct sockaddr_in local; local.sin_family=AF_INET; local.sin_port=htons(atoi(argv[2])); local.sin_addr.s_addr=inet_addr(argv[1]); if(connect(sock,(struct sockaddr*)&local,sizeof(local))<0) { perror("connect"); return 3; } char buf[1024]; ssize_t _s; while(1) { printf("please input\n"); fflush(stdout); _s=read(0,buf,sizeof(buf)-1); if(_s>0) { buf[_s]=‘\0‘; if(strncmp(buf,"quit",4)==0) { close(sock); return 0; } write(sock,buf,strlen(buf)); } else if(_s==0) { close(sock); return 1; } _s=read(sock,buf,sizeof(buf)-1); if(_s>0) { buf[_s]=‘\0‘; printf("echo:%s\n",buf); } } return 0; }
运行截图:
client:
server:
本文出自 “小止” 博客,请务必保留此出处http://10541556.blog.51cto.com/10531556/1783832
以上是关于epoll实现TCP通信的主要内容,如果未能解决你的问题,请参考以下文章
linux epoll机制对TCP 客户端和服务端的监听C代码通用框架实现