1,server端的fd不需要设置et模式 我们在创建socket成功后会有个listenfd,listenfd = socket(AF_INET, SOCK_STREAM, 0) 然后会把这个fd加入epoll wait队列中,网上很多没有经过验证的代码是这样写的: ev.data.fd = listenfd; ev.events=EPOLLIN|EPOLLET; epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev); 这样会导致服务器在并发处理客户端连接时,丢失部分连接,或者说丢失epoll事件 表现为:采用netstat查看网络,会看到Recv-Q大于0,但是程序执行不到accept代码段。 这个也和具体内核有关系,但是最好代码方面不要这么写。 正确的写法为:ev.events=EPOLLIN; 这里不用指定EPOLLET 2,如果采用et模式,读的时候要读完数据 3,要处理发送,接收函数的返回值和错误码。如:read,recv,send,write,errno write如果返回-1,可能是写缓冲满,需要等待out事件再写入 下面贴个基本模型,代码不能运行,仅参考 struct _sgc_epoll_socketserver_config_s{ char * local_addr;// 监听的本地地址 int port;// 监听的端口 int epfd;// epoll 文件描述符 size_t buffer_size;// 缓冲区大小,单位byte int maxclients;// 最多允许连接的客户端数 sgc_epoll_client_new_handler_t handler_new;// 有新连接处理函数 sgc_epoll_client_msg_handler_t hander_read;// 读取数据 sgc_epoll_client_close_hander_t hander_close;// 断开 }; /** * 启动socket服务 */ void sgc_serversocket_start(sgc_epoll_socketserver_config_t *config) { int epfd, listenfd,connfd,readyfds,i,sockfd, clients, nreadbytes; int flag=1,len=sizeof(int); int cfg_sndbuff = 1024 * 1024;// 1M int cfg_revbuffer = 1024 * 1024; // 1M struct epoll_event ev, events[config->maxclients]; struct sockaddr_in clientaddr; struct sockaddr_in serveraddr; socklen_t clilen = sizeof(struct sockaddr_in); if (config->handler_new == NULL || config->hander_read == NULL){ sgc_log_error("config error hand is NULL\n"); exit(1); } char * buffer = (char *)malloc(config->buffer_size); memset(buffer, 0, config->buffer_size); epfd = epoll_create(config->maxclients); config->epfd = epfd; listenfd = socket(AF_INET, SOCK_STREAM, 0); if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &flag, len)) sgc_log_error("set sockopt SO_REUSEADDR error\n"); if (setsockopt(listenfd, SOL_SOCKET, SO_SNDBUF, &cfg_sndbuff, len)) sgc_log_error("set sockopt SO_SNDBUF error\n"); if (setsockopt(listenfd, SOL_SOCKET, SO_SNDBUF, &cfg_revbuffer, len)) sgc_log_error("set sockopt SO_SNDBUF error\n"); setnonblocking(listenfd); ev.data.fd = listenfd; ev.events=EPOLLIN; // 监听端口 epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev); bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; char *local_addr= config->local_addr; inet_aton(local_addr,&(serveraddr.sin_addr)); serveraddr.sin_port=htons(config->port); if (bind(listenfd,(struct sockaddr *)&serveraddr, sizeof(serveraddr))) { sgc_log_error("bind fail %s:%d\n", config->local_addr, config->port); exit(1); } sgc_log_debug("start listen %s:%d backlog:%d\n", local_addr, config->port, LISTEN_BACKLOG); if (listen(listenfd, LISTEN_BACKLOG)) { sgc_log_error("listen %s:%d max:%d error\n", config->local_addr, config->port, config->maxclients); exit(1); } clients = 0; for (;;) { readyfds=epoll_wait(epfd,events,config->maxclients,EPOLL_WAIT_TIMEOUT); if (readyfds < 0){ sgc_log_error("epoll_weit errno:%d %s\n", errno, strerror(errno)); }else if (readyfds > 0) sgc_log_debug("readyfds:%d errno:%d err:%s\n", readyfds, errno, strerror(errno)); for(i=0;i<readyfds;++i) { // 新用户连接 if(events[i].data.fd==listenfd) { if (clients >= config->maxclients){ sgc_log_debug("clients full,close:%s socket fd:%d clients:%d\n", inet_ntoa(clientaddr.sin_addr), listenfd, clients); continue; }else{ clients++; connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clilen); if(connfd<0){ sgc_log_error("accept error connfd<0 fd=%d errno:%d %s\n", connfd, errno, strerror(errno)); exit(1); } setnonblocking(connfd); ev.data.fd=connfd; ev.events=EPOLLIN|EPOLLET; sgc_log_debug("new clients:%d, num:%d %s\n", connfd, clients, inet_ntoa(clientaddr.sin_addr)); config->handler_new(connfd, &clientaddr, config); epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); } }else if(events[i].events & EPOLLIN)//如果是已经连接的用户,并且收到数据,那么进行读入。 { if ((sockfd = events[i].data.fd) < 0){ sgc_log_error("EPOLLIN fd < 0\n"); continue; } // 一次读完缓冲区 memset(buffer, 0, config->buffer_size); while((nreadbytes = recv(sockfd, buffer, config->buffer_size, 0)) > 0) { sgc_log_debug("read fd:%d len:%d\n", sockfd, nreadbytes); config->hander_read(sockfd, buffer, nreadbytes); memset(buffer, 0, config->buffer_size); } sgc_log_debug("read finish nreadbytes:%d fd:%d errno:%d, err:%s\n", nreadbytes, sockfd, errno, strerror(errno)); if (nreadbytes < 0){ if (errno == ECONNRESET){ EPOLL_CLOSE_CODE("connrset") } }else if (nreadbytes == 0){ EPOLL_CLOSE_CODE("normal") } }else{// 其它异常情况,关闭 sockfd = events[i].data.fd; EPOLL_CLOSE_CODE("exception") } } } } |
LINUX EPOLL小结
最新推荐文章于 2022-11-24 15:02:36 发布