epoll听说很厉害的,走马观花的研究了下,好像是很牛。具体没空细搞了,哥最近准备动动,作为一个不务正业的DBA,数据库和UNIX是时候
得温习温习了。所以匆匆实现了个基于epoll的服务器作为最近socket研究的一个暂时的终结。
其实最近一口气整了整socket收获还是很大的,虽然还是有很多不理解和疑问,也没关系了,这么深的水,留着以后再说。至少概念又清楚了很多,
基础打的牢,那些libevent,asio之流理解起来那还不是很容易的事?
关于epoll的相关知识我不就写了,只把我弄的例子写下,我整的这个epoll服务器其实就只是把man epoll中举得那个例子给做完善了。
主要参考了man epoll和百度百科中关于epoll的介绍来实现的。
服务器代码:
/************************************************* Author: xiongchuanliang Description: 基于epoll的服务器,就是把man epoll中的例子补充完整了。 编译命令: Linux: g++ -o tcpepoll2 tcpepoll2.cpp -m64 -I./common **************************************************/ // 服务端代码 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "initsock.h" #include "common.h" #include <sys/epoll.h> CInitSock initSock; #define MAX_EVENTS 10 int do_use_fd(int client); //与客户端交互 int setnonblocking(int sock); // 设置套接字为不阻塞 int main(int argc, char* argv[]) { int n = 0; //创建套接字 SOCKET sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if(sListen == INVALID_SOCKET) { PrintError("socket() failed.\n"); exit(EXIT_FAILURE); } //绑定本地IP和端口到套接字 struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVPORT); //大于1024且小于65535 server_addr.sin_addr.s_addr = INADDR_ANY; bzero(&(server_addr.sin_zero),8); //SO_REUSEADDR : 使bind函数能允许地址立即重用 int on = 1; setsockopt( sListen, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on) ); if(bind(sListen,(struct sockaddr *)&server_addr,sizeof(struct sockaddr)) == SOCKET_ERROR) { PrintError("bind() failed."); exit(EXIT_FAILURE); } //开始监听 if(listen(sListen, BACKLOG) == SOCKET_ERROR) { PrintError("sListen() failed."); exit(EXIT_FAILURE); } struct epoll_event ev,events[MAX_EVENTS]; SOCKET conn_sock, nfds, epollfd; //listen_sock //指定内核需要监听的描述数 //函数原型:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); epollfd = epoll_create(10); if (epollfd == -1) { PrintError("epoll_create"); exit(EXIT_FAILURE); } /* EPOLLLT 默认行为,只要一个文件描述符处于就绪状态,epoll 就会不停的通知你有事件发生。传统的 select/poll 都是这样处理。 EPOLLET 属新方式,只在一个文件描述符新处于就绪的时候通知一次,之后不管数据有没有读完,都不会再通知,当然,有新数据到还是会通知的。 所以,用 EPOLLET 的时候,一定要把文件描述符设置为 non-blocking, 而且最好是一直读数据,读到返回 EAGAIN 才停下 */ //指定内核需要监听的事件,并注册一个新的fd到epoll_create()返回的epollfd中 ev.events = EPOLLIN | EPOLLET; //对读感兴趣,边沿触发模式 ev.data.fd = sListen; //函数原型:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sListen, &ev) == -1) { PrintError("epoll_ctl: listen_sock"); exit(EXIT_FAILURE); } socklen_t nAddrlen = sizeof(struct sockaddr_in); for (;;) { //函数原型: int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); //MAX_EVENTS的值不能大于 epoll_create()指定的值 //等待epoll_ctl指定的监听的事件发生 nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); if (nfds == -1) { PrintError("epoll_pwait()"); } //遍历发生了指定监听的事件事件的fd for (n = 0; n < nfds; ++n) { // 如果是主socket的事件的话,则表示 // 有新连接进入了,进行新连接的处理。 if (events[n].data.fd == sListen) { //连接指定的客户端 // conn_sock = accept(sListen,(struct sockaddr *) &local, &addrlen); conn_sock = accept(sListen,(struct sockaddr *) &server_addr, &nAddrlen); if (conn_sock == -1) { PrintError("accept()"); continue; } // 将新连接设置为非阻塞模式 setnonblocking(conn_sock); //设置event为监控读操作,即客户端有发送数据过来。 ev.events = EPOLLIN | EPOLLET; ev.data.fd = conn_sock; //将新设置的这个event加入到epoll的监听队列里 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) { PrintError("epoll_ctl: conn_sock"); } }else{ do_use_fd(events[n].data.fd); } } //end for (n = 0; n < nfds; ++n) } //end for (;;) //关闭监听套接字 close(sListen); //关闭epoll句柄 close(epollfd); exit(EXIT_SUCCESS); } //与客户端交互 int do_use_fd(int client) { char recvData[MAXDATASIZE]={0}; int recvbytes = read(client,recvData, MAXDATASIZE); if( recvbytes == 0) { printf("read() no data!\n"); }else if( recvbytes < 0) { if (errno != EAGAIN) { PrintError("read() failed."); close (client); } }else if( recvbytes > 0) { recvData[recvbytes]=‘\0‘; printf("msg:%s\n",recvData); char sendData[MAXDATASIZE]={0}; strcpy(sendData,"Hello client!\n"); int sendbytes = write(client, sendData, sizeof(sendData)) ; if( sendbytes < 0) { PrintError("write() failed."); close (client); }else{ printf("write() success! %d \n",sendbytes); } } } // 设置套接字为不阻塞 int setnonblocking(int sock) { int flags = fcntl (sock, F_GETFL, 0); if (flags == -1) { PrintError("fcntl() failed."); return -1; } flags |= O_NONBLOCK; if ( (fcntl (sock, F_SETFL, flags)) == -1) { PrintError("fcntl() failed. O_NONBLOCK"); return -1; } return 0; }
MAIL: xcl_168@aliyun.com
BLOG: http://blog.csdn.net/xcl168
原文:http://blog.csdn.net/xcl168/article/details/19302693