select和epoll是最常用的两种多路IO复用技术,select是跨平台的函数,而epoll是linux独有的。 select特点: 1.监听的文件描述符有限制,最多只能监听1024个描述符. 2.在监听到客户端事件发生时,处理每个事件,都需要循环遍历,才能确认是哪个客户端产生读事件。效率比较低。 3.适合并发连接数较少的服务器。 相关函数:
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout); void FD_CLR(int fd, fd_set *set); int FD_ISSET(int fd, fd_set *set); void FD_SET(int fd, fd_set *set); void FD_ZERO(fd_set *set);epoll特点: 1.只要内存足够,能监听足够多的文件描述符,由自己设定 2.当监听到客户端事件发生时,会将监听到的时间存在数组中,并且该数组存有客户端fd的信息,无需循环遍历确认。 3.适合高并发的服务器 相关函数:
int epoll_create(int size); size:只要大于0即可int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); op: EPOLL_CTL_ADD 添加监听描述符 EPOLL_CTL_MOD 修改监听描述符 EPOLL_CTL_DEL 删除监听描述符 typedef union epoll_data { void *ptr; int fd; } epoll_data_t; struct epoll_event { uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; events: EPOLLIN 读事件 EPOLLOUT 写事件 EPOLLERR 错误事件 EPOLLET 边沿触发 EPOLLONESHOT 一次触发(注意:触发完如果还想被监听到,需要重新mod一下监听描述符)先贴上自定义分装的sock.h 函数
select服务器模型:
#include<arpa/inet.h>#include <stdio.h>#include <sys/socket.h>#include <sys/types.h>#include <netinet/in.h>#include <errno.h>#include <string.h>#include <stdlib.h>#include<sys/stat.h>#include<fcntl.h>#include <unistd.h>int create_server(unsigned short port, const char* ip, int backlog){ int fd = socket(AF_INET, SOCK_STREAM, 0); if(fd < 0) { perror("socket"); return fd; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(ip); // 监听的ip地址 int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr)); if(ret < 0) { perror("bind"); return -2; } listen(fd, backlog); return fd;}int doAccept(int fd, struct sockaddr* addr, socklen_t* addrlen){ while(1) { int newfd = accept(fd, addr, addrlen); if(newfd < 0 && errno == EINTR) { continue; } return newfd; } return -1;}int main(){ int server = create_server(9988, "0.0.0.0", 250); int maxfd = server; list<int> clients; // 保存所有的客户连接socket while(1) { fd_set set; struct timeval tv; maxfd = server; FD_ZERO(&set); FD_SET(server, &set); for(list<int>::iterator it = clients.begin(); it!=clients.end(); ++it) { FD_SET(*it, &set); if(*it > maxfd) maxfd = *it; // 寻找最大的文件描述符 } tv.tv_sec = 2; tv.tv_usec = 0; int ret = select(maxfd+1, &set, NULL, NULL, &tv); if(ret > 0) { if(FD_ISSET(server, &set)) { // 下一次监听时,需要将它放入集合 int newfd = doAccept(server, NULL, NULL); clients.push_back(newfd); } for(list<int>::iterator it = clients.begin(); it!=clients.end(); ) { int newfd = *it; if(FD_ISSET(newfd, &set)) { char buf[1024]; ret = read(newfd, buf, sizeof(buf)); if(ret > 0) { PRintf("%s, fd=%d/n", buf, newfd); } else if(ret < 0 && errno == EINTR) { //被打断暂时不处理 } else // ==0 或者 <0并且错误吗不是EINTR { // 当对方关闭socket,或者socket有问题时,应该关闭对应的socket close(newfd); it = clients.erase(it); continue; } } it++; } } }}epoll服务器模型
#include<arpa/inet.h>#include <stdio.h>#include <sys/socket.h>#include <sys/types.h>#include <netinet/in.h>#include <errno.h>#include <string.h>#include <stdlib.h>#include<sys/stat.h>#include<fcntl.h>#include <unistd.h>#include <sys/epoll.h>//封装epoll_ctl函数void epoll_add(int epollfd, int fd, int events){ struct epoll_event ev; ev.data.fd = fd; ev.events = events; epoll_ctl(epollfd, EPOLL_CTL_ADD, server, &ev);}int create_server(unsigned short port, const char* ip, int backlog){ int fd = socket(AF_INET, SOCK_STREAM, 0); if(fd < 0) { perror("socket"); return fd; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(ip); // 监听的ip地址 int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr)); if(ret < 0) { perror("bind"); return -2; } listen(fd, backlog); return fd;}int main(){ int server = create_server(9988, "0.0.0.0", 250); // epollfd也是一个fd,那能不能把epollfd放到另外一个epollfd中去 int epollfd = epoll_create(512); epoll_add(epollfd, server, EPOLLIN); struct epoll_event ev_out[8]; while(1) { int ret = epoll_wait(epollfd, ev_out, 8, 2000); if(ret > 0)//监听事件发生 { int i; for(i=0; i<ret; ++i) { int fd = ev_out[i].data.fd; if(fd == server) { int newfd = accept(fd, NULL, NULL); epoll_add(epollfd, newfd, EPOLLIN); } else // { char buf[1024]; int r = read(fd, buf, sizeof(buf)); if(r > 0) { printf("%s/n", buf); } else if(r < 0 && errno == EINTR) { } else // r == 0 || (r< 0 && errno != EINTR) { close(fd); } } } } }}新闻热点
疑难解答