做书app下载网站有哪些内容,培训机构,关键词挖掘ppt,网站图片要多少像素IO多路转接模型#xff1a;select/poll/epoll
对大量描述符进行事件监控(可读/可写/异常)
select模型 用户定义描述符的事件监控集合 fd_set#xff08;这是一个位图#xff0c;用于存储要监控的描述符#xff09;; 用户将需要监控的描述符添加到集合中#xff0c;这个描…IO多路转接模型select/poll/epoll
对大量描述符进行事件监控(可读/可写/异常)
select模型 用户定义描述符的事件监控集合 fd_set这是一个位图用于存储要监控的描述符; 用户将需要监控的描述符添加到集合中这个描述符集合的大小取决于一个宏 _FD_SETSIZE 1024将集合拷贝到内核中进行监控在内核中对所有描述符进行轮询遍历判断是否有关心的事件就绪若有描述符就绪从监控集合中将未就绪的描述符移除然后调用返回返回给用户就绪描述符饥集合用户遍历所有描述符判断描述符是否在集合中若在集合中则这个描述符是就绪描述符用户针对这个就绪的描述符事件进行相应的处理用户仅仅对大量描述符中就绪的描述符进行处理sock程序就可以避免accept/recv处因为没有数据到来而阻塞
#include sys/select.h
/* According to earlier standards */
#include sys/time.h
#include sys/types.h
#include unistd.h int select(int nfds, fd_set *readfds,fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);nfds: 监控的文件描述符集里最大文件描述符加1因为此参数会告诉内核检测前多少个文件描述符的状态readfds 监控有读数据到达文件描述符集合传入传出参数writefds 监控写数据到达文件描述符集合传入传出参数exceptfds 监控异常发生达文件描述符集合,如带外数据到达异常传入传出参数timeout 定时阻塞监控时间3种情况 NULL永远等下去设置timeval等待固定时间设置timeval里时间均为0检查描述字后立即返回轮询
struct timeval
{
long tv_sec; /* seconds */ //秒
long tv_usec; /* microseconds */ //微秒
}; void FD_CLR(int fd, fd_set *set); //把文件描述符集合里fd清0 将指定的描述符从集合中移除int FD_ISSET(int fd, fd_set *set); //测试文件描述符集合里fd是否置1 判断指定的描述符是否在集合中void FD_SET(int fd, fd_set *set); //把文件描述符集合里fd位置1 将指定的描述符添加到集合中void FD_ZERO(fd_set *set); //把文件描述符集合里所有位清0 清空描述符集合
select优缺点
select 能监听的文件描述符个数受限于 FD_SETSIZE,一般为 1024单纯改变进程打开的文件描述符个数并不 能改变 select 监听文件个数每次都需要重新将监控集合拷贝到内核select会修改集合解决 1024 以下客户端时使用 select 是很合适的但如果链接客户端过多select 采用的是轮询模型会大大降低服务器响应效率select返回给用户就绪的描述符集合将未就绪的描述符从集合中移除但是并没有告诉用户具体哪一个描述符就绪需要用户遍历描述符是否在集合中来判断哪个描述符就绪这个判断是一个遍历的过程性能随着描述符增多而下降并且复杂度更高select每次返回都会修改监控集合因此每次都需要用户重新向集合中添加所有描述符select遵循posix标准支持跨平台监控的超时等待时间可以精细到微秒
class Select
{
public:Add(TcpSocket sock); //将用户关心socket描述符添加到监控集合中Del(TcpSocket sock); //从监控集合中移除不再关心的socket描述符Wait(std::vectorTcpSocketlist,init timeout_sec,int timeout_sec); //从开始监控并且向用户返回就绪的socket
private:fd_set _rfds;int_max_fd;
};实现
select服务端
/** 这个文件封装一个select类向外界提供更加简单点的select监控接口* 将用户关心socket描述符添加到监控集合中* 从监控集合中移除不再关心的socket描述符* 从开始监控并且向用户返回就绪的socket*/#includevector
#includesys/select.h
#includetcpsocket.hppclass Select
{public:Select(): _max_fd (-1){FD_ZERO(_rfds);//清空集合} bool Add(TcpSocket sock){int fd sock.GetFd();//void FD_SET(int fd,fd_set *set)//向set描述符集合中添加fd描述符FD_SET(fd,_rfds);_max_fd _max_fd fd ? _max_fd : fd; return true;} bool Del(TcpSocket sock){int fd sock.GetFd();//void FD_CLR(int fd, fd_set *set)//从set描述符集合中移除FD_CLR(fd,_rfds);//从最大的往前遍历for(int i _max_fd ; i 0; i--){//int FD_ISSET(int fd, fd_set *set);//判断fd描述符是否还在set集合中if(FD_ISSET(i,_rfds)){_max_fd i;break;}}} bool Wait(std::vectorTcpSocketlist,int timeout_sec 3){struct timeval tv; //超时时间tv.tv_sec timeout_sec;tv.tv_usec 0;fd_set set _rfds;int ret select(_max_fd 1, set, NULL ,NULL,tv);if(ret 0){perror(select error);return false;}else if(ret 0){std::cout select wait timeout\n;return false;}for(int i 0 ;i _max_fd; i){if(FD_ISSET(i,set)){TcpSocket sock;sock.SetFd(i);list.push_back(sock);}}return true;} private:fd_set _rfds;int _max_fd;
};int main()
{TcpSocket sock;CHECK_RET(sock.Socket());CHECK_RET(sock.Bind(192.168.145.132,9000));CHECK_RET(sock.Listen());Select s;s.Add(sock); while(1){std::vectorTcpSocketlist;if(s.Wait(list)false){continue;}for(int i 0 ; i list.size(); i){//判读socket是监听socket还是通信socketif(list[i].GetFd() sock.GetFd()){TcpSocket clisock;std::string cli_ip;uint16_t cli_port;if(sock.Accept(clisock,cli_ip,cli_port) false){continue;}s.Add(clisock);}else{std::string buf;if(list[i].Recv(buf) false){s.Del(list[i]);list[i].Close();continue;}std::coutclient say:buf \n;}}}sock.Close();return 0;
} 客户端 #include signal.h
#include tcpsocket.hppvoid sigcb(int signo){printf(connection closed\n);
}
int main(int argc, char *argv[])
{if (argc ! 3) {std::cout./tcp_cli 192.168.122.132 9000\n;return -1; } std::string ip argv[1];uint16_t port atoi(argv[2]);signal(SIGPIPE, sigcb);TcpSocket sock;CHECK_RET(sock.Socket());CHECK_RET(sock.Connect(ip, port));while(1) {std::string buf;std::cout client say:;fflush(stdout);std::cin buf;sock.Send(buf);}sock.Close();return 0;
} poll模型
poll函数接口
#include poll.hint poll(struct pollfd *fds, nfds_t nfds, int timeout);// pollfd结构
struct pollfd { int fd; /* 用户监控的描述符 */ short events; /* 描述符关心的事件 POLLIN/POLLOUT */ short revents; /* 描述符实际就绪的事件 */ }参数说明
fds是一个poll函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返回的事件集合. 描述事件结构数组nfds表示fds数组的长度. 要监控事件个数timeout表示poll函数的超时时间, 单位是毫秒(ms).
events和revents的取值:
POLLIN 普通或带外优先数据可读,即POLLRDNORM | POLLRDBANDPOLLRDNORM 数据可读POLLRDBAND 优先级带数据可读POLLPRI 高优先级可读数据POLLOUT 普通或带外数据可写POLLWRNORM 数据可写POLLWRBAND 优先级带数据可写POLLERR 发生错误POLLHUP 发生挂起POLLNVAL 描述字不是一个打开的文件
实现原理
用户定义描述符事件数组向数组中添加关心的描述符事件将pollfd事件数组拷贝到内核中进行遍历轮询监控判断是否就绪了关心的事件将描述符实际就绪的事件信息标记到revents中当poll返回。用户遍历pollfd事件数组通过revents判断描述符就绪了什么事件进而进行相应操作
使用poll监控标准输入
#include poll.h
#include unistd.h
#include stdio.hint main() { struct pollfd poll_fd; //一个结构就是一个事件poll_fd.fd 0; poll_fd.events POLLIN; //可读事件for (;;) { //开始监控int ret poll(poll_fd, 1, 1000); //遍历轮询 if (ret 0) { //出错perror(poll); continue; }if (ret 0) { //超时printf(poll timeout\n); continue; } //ret0if (poll_fd.revents POLLIN) { //看事件是否为我们所关心的事件 //对事件进行操作 char buf[1024] {0}; read(0, buf, sizeof(buf) - 1); printf(stdin:%s, buf); } }
} poll有缺点分析
优点
poll采用事件结构形式对描述符关心的事件进行监控简化了select三种集合操作的流程poll没有描述符上限的设置
缺点
不能跨平台只能用于Linux下在内核中进行轮询遍历判断就绪性能随着描述符事件增多而下降也不会告诉用户具体哪一个描述符就绪需要用户轮询遍历判断事件中的revents进而对描述符进行相应事件操作 revents POLLIN/POLLOUT需要每次都向内核中拷贝监控信息