/* * File: main.c * Author: xys * * Created on 2013年12月12日, 下午3:14 * * 实现功能:通过epoll,处理多个socket请求 * 监听一个端口,监听到有链接时,添加epoll_event * * 命令行下 telnet localhost 3304 */ #include <string.h> #include <strings.h> #include <stdio.h> #include <stdlib.h> #include <sys/epoll.h> #include <sys/socket.h> #include <netinet/in.h> #include <errno.h> #include <fcntl.h> #define MYPORT 3304 //最多处理的connect #define MAX_EVENTS 500 //当前连接数 int currentClinet = 0; //数据接受buf #define REVLEN 1024 char recvBuf[REVLEN]; //epoll相关 //epoll 描述符 int epollfd; //事件数组 struct epoll_event eventList[MAX_EVENTS]; void AcceptConn(int srvfd); void RecvData(int fd); void setSockNonBlock(int sock); int main(int argc, char** argv) { int sockListen; struct sockaddr_in server_addr; struct epoll_event event; int yes = 1; //socket if((sockListen = socket(AF_INET, SOCK_STREAM, 0)) < 0){ printf("socket error.\n"); return -1; } //in case of 'address already in use' error message if (setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))) { perror("setsockopt failed"); exit(EXIT_FAILURE); } //设置sock为non-blocking setSockNonBlock(sockListen); //创建要bind的socket address bzero(&server_addr,sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(MYPORT); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //bind if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){ printf("bind error.\n"); return -1; } //listen if(listen(sockListen, 5) < 0){ printf("listen error.\n"); return -1; } //1.epoll 初始化 epollfd = epoll_create(MAX_EVENTS); if(epollfd == -1){ perror("epoll create failed."); exit(EXIT_FAILURE); } event.events = EPOLLIN|EPOLLET; event.data.fd = sockListen; //2.epoll_ctrl if(epoll_ctl(epollfd, EPOLL_CTL_ADD, sockListen, &event) < 0){ printf("epoll add fail : fd = %d\n", sockListen); return -1; } //epoll while(1){ int n = 0; int timeout = 3000; //3.epoll_wait int ret = epoll_wait(epollfd, eventList, MAX_EVENTS, timeout); if(ret < 0){ if(errno == EINTR && epollfd > 0){ usleep(1*1000); continue; } printf("epoll wait failed\n"); break; }else if(ret == 0){ fprintf(stderr, "no socket ready for read within %d secs\n", timeout/1000); sleep(1); continue; } //直接获取了事件的量,给出了活动的流,这里是和poll区别的关键 //检测到ret个IO file descriptor的events,loop各个fd进行响应 for(n = 0; n < ret; n++){ //错误退出 /*events[n]即为检测到的event,域events[n].events表示具体哪些events, 域events[n].data.fd即对应的IO fd*/ if((eventList[n].events & EPOLLERR) || (eventList[n].events & EPOLLHUP) || !(eventList[n].events & EPOLLIN)) { //由于events[i].events使用每个bit表示event,因此判断是否包含某个具体事件可以使用`&`操作符 printf("epoll error.\n"); close(eventList[n].data.fd); return -1; } //对检测到event的各IO fd进行响应 if(eventList[n].data.fd == sockListen){ //当前fd是server的socket,不进行读而是accept所有client连接请求 AcceptConn(sockListen); }else{ //当前fd是client连接的socket,可以读(read from client) RecvData(eventList[n].data.fd); //不删除 //epoll_ctl(epollfd, EPOLL_CTL_DEL, eventList[n].data.fd, &eventList[n]); } } } close(epollfd); close(sockListen); printf("test.\n"); return 0; } /************************************************** 函数名:AcceptConn 功能:接受客户端的链接 参数:srvfd:监听SOCKET ***************************************************/ void AcceptConn(int srvfd) { int confd; struct epoll_event event; char client_ip_str[INET_ADDRSTRLEN]; struct sockaddr_in sin; socklen_t len = sizeof(struct sockaddr_in); bzero(&sin, len); confd = accept(srvfd, (struct sockaddr*)&sin, &len); if (confd == -1) { if ( (errno == EAGAIN) || (errno == EWOULDBLOCK) ) { //non-blocking模式下无新connection请求,跳出while (1) return; } else { perror("accept failed"); exit(EXIT_FAILURE); } } if (!inet_ntop(AF_INET, &(sin.sin_addr), client_ip_str, sizeof(client_ip_str))) { perror("inet_ntop failed"); exit(EXIT_FAILURE); } printf("accept a client from: %s, client=%d\n", client_ip_str, confd); //设置conn_sock为non-blocking setSockNonBlock(confd); //4.epoll_wait //将新建的链接添加到EPOLL的监听中 event.data.fd = confd; event.events = EPOLLIN|EPOLLET; if ( epoll_ctl(epollfd, EPOLL_CTL_ADD, confd, &event) == -1 ) { perror("epoll_ctl(EPOLL_CTL_ADD) failed\n"); exit(EXIT_FAILURE); } } //读取数据 void RecvData(int fd) { int ret; memset(recvBuf, 0, REVLEN); printf("RecvData function.\n"); //recv数据 ret = recv(fd, (char *)recvBuf, sizeof(recvBuf), 0); if(ret == 0){ return; } //数据接受完毕 printf("client=%d,buf=%s\n", fd, recvBuf); } //函数:设置sock为non-blocking mode void setSockNonBlock(int sock) { int flags; flags = fcntl(sock, F_GETFL, 0); if (flags < 0) { perror("fcntl(F_GETFL) failed"); exit(EXIT_FAILURE); } if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { perror("fcntl(F_SETFL) failed"); exit(EXIT_FAILURE); } }
socket epoll 模型
最新推荐文章于 2024-03-08 17:31:01 发布