IO多路转接模型----(select的模型,select的优缺点,poll的模型,poll的优缺点)

IO多路转接模型:select/poll/epoll

对大量描述符进行事件监控(可读/可写/异常)

select模型

在这里插入图片描述

  1. 用户定义描述符的事件监控集合 fd_set(这是一个位图,用于存储要监控的描述符); 用户将需要监控的描述符添加到集合中,这个描述符集合的大小取决于一个宏 _FD_SETSIZE = 1024
  2. 将集合拷贝到内核中进行监控;在内核中对所有描述符进行轮询遍历判断是否有关心的事件就绪
  3. 若有描述符就绪,从监控集合中将未就绪的描述符移除;然后调用返回(返回给用户就绪描述符饥集合)
  4. 用户遍历所有描述符,判断描述符是否在集合中,若在集合中,则这个描述符是就绪描述符
  5. 用户针对这个就绪的描述符事件进行相应的处理,用户仅仅对大量描述符中就绪的描述符进行处理,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种情况
    1. NULL,永远等下去
    2. 设置timeval,等待固定时间
    3. 设置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优缺点

  1. select 能监听的文件描述符个数受限于 FD_SETSIZE,一般为 1024,单纯改变进程打开的文件描述符个数并不 能改变 select 监听文件个数
  2. 每次都需要重新将监控集合拷贝到内核(select会修改集合)
  3. 解决 1024 以下客户端时使用 select 是很合适的,但如果链接客户端过多,select 采用的是轮询模型,会大大降低服务器响应效率
  4. select返回给用户就绪的描述符集合(将未就绪的描述符从集合中移除),但是并没有告诉用户具体哪一个描述符就绪,需要用户遍历描述符是否在集合中来判断哪个描述符就绪,这个判断是一个遍历的过程,性能随着描述符增多而下降,并且复杂度更高
  5. select每次返回都会修改监控集合,因此每次都需要用户重新向集合中添加所有描述符
  6. select遵循posix标准,支持跨平台;
  7. 监控的超时等待时间可以精细到微秒
class Select
{
public:Add(TcpSocket &sock); //将用户关心socket描述符添加到监控集合中Del(TcpSocket &sock); //从监控集合中移除不再关心的socket描述符Wait(std::vector<TcpSocket>&list,init timeout_sec,int timeout_sec); //从开始监控,并且向用户返回就绪的socket
private:fd_set _rfds;int_max_fd;
};

实现

select服务端

/** 这个文件封装一个select类,向外界提供更加简单点的select监控接口*  将用户关心socket描述符添加到监控集合中* 从监控集合中移除不再关心的socket描述符* 从开始监控,并且向用户返回就绪的socket*/#include<vector>
#include<sys/select.h>
#include"tcpsocket.hpp"class 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::vector<TcpSocket>&list,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::vector<TcpSocket>list;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::cout<<"client say:"<<buf <"\n";}}}sock.Close();return 0;
}    

客户端

  
#include <signal.h>
#include "tcpsocket.hpp"void 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.h>int 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的取值:
  1. POLLIN 普通或带外优先数据可读,即POLLRDNORM | POLLRDBAND
  2. POLLRDNORM 数据可读
  3. POLLRDBAND 优先级带数据可读
  4. POLLPRI 高优先级可读数据
  5. POLLOUT 普通或带外数据可写
  6. POLLWRNORM 数据可写
  7. POLLWRBAND 优先级带数据可写
  8. POLLERR 发生错误
  9. POLLHUP 发生挂起
  10. POLLNVAL 描述字不是一个打开的文件

实现原理

  1. 用户定义描述符事件数组,向数组中添加关心的描述符事件
  2. 将pollfd事件数组,拷贝到内核中进行遍历轮询监控,判断是否就绪了关心的事件
  3. 将描述符实际就绪的事件信息,标记到revents中
  4. 当poll返回。用户遍历pollfd事件数组,通过revents判断描述符就绪了什么事件,进而进行相应操作

使用poll监控标准输入

#include <poll.h>
#include <unistd.h>
#include <stdio.h>int 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;   } //ret>0if (poll_fd.revents == POLLIN) {  //看事件是否为我们所关心的事件 //对事件进行操作    char buf[1024] = {0};      read(0, buf, sizeof(buf) - 1);     printf("stdin:%s", buf);    }  } 
} 

poll有缺点分析

优点:
  1. poll采用事件结构形式对描述符关心的事件进行监控,简化了select三种集合操作的流程
  2. poll没有描述符上限的设置
缺点
  1. 不能跨平台,只能用于Linux下
  2. 在内核中进行轮询遍历判断就绪,性能随着描述符事件增多而下降
  3. 也不会告诉用户具体哪一个描述符就绪,需要用户轮询遍历判断事件中的revents;进而对描述符进行相应事件操作 revents & POLLIN/POLLOUT
  4. 需要每次都向内核中拷贝监控信息

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/383182.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IO多路转接模型-----epoll

epoll&#xff1a; Linux下性能最高的多路转接模型 epoll 有3个相关的系统调用. epoll_create 功能&#xff1a;创建epoll&#xff0c;在内核中创建eventpoll结构体&#xff0c;size决定了epoll最多监控多少个描述符&#xff0c;在Linux2.6.8之后被忽略&#xff0c;但是必须…

再写单链表(不带头单链表)

单链表 实际中链表的结构非常多样&#xff0c;以下情况组合起来就有8种链表结构&#xff1a; 单向、双向带头、不带头循环、非循环 虽然有这么多的链表的结构&#xff0c;但是我们实际中最常用还是两种结构&#xff1a; 无头单向非循环链表&#xff1a;结构简单&#xff0…

再写双向循环链表

#pragma once #include<assert.h> #include<malloc.h> #include<stdio.h> typedef int DLDataType;//定义链表结点结构 typedef struct DListNode{DLDataType value;struct DListNode *prev; //指向前一个结点struct DListNode *next; //指向后一个结点 } DL…

链表题目--1 删除链表中所有等于val的值

注意事项 要删除的结点相邻第一个结点就是要删除的结点 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* removeElements(struct ListNode* head, int val){if(headNULL){return NULL;}struct …

链表题目--2 求链表的中间结点 和 求链表中倒数第k个结点

求链表的中间结点 思路 一个走两步&#xff0c;一个走一步。一个走到尾&#xff0c;另外一个就走到了中间 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* middleNode(struct ListNode* head…

链表题目---3 合并两个有序单链表 和 分割链表

合并两个有序单链表 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){if(l1 NULL){return l2;}else if(l2 NULL){return l1;}struc…

链表题目---4 删除链表中重复的结点 和 判断链表是否为回文链表

删除链表中重复的结点 /* struct ListNode {int val;struct ListNode *next;ListNode(int x) :val(x), next(NULL) {} }; */ class Solution { public:ListNode* deleteDuplication(ListNode* pHead){if(pHead NULL){return NULL;}ListNode *prev NULL; //用于删除的结点, 是…

链表题目----5 相交链表 和 环形链表 和 返回链表开始入环的第一个节点

相交链表 思路 链表交叉不可能是x型因为有可能两个链表不等长&#xff0c;所以我们必须让他们从同一起跑位置去起跑从同一起跑位置出发&#xff0c;依次比较每个结点的地址是否相同 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct L…

链表题目---6 复制带随机指针的链表

思路 将新结点放在老结点的后面 复制random 将链表拆开 /* // Definition for a Node. class Node { public:int val;Node* next;Node* random;Node() {}Node(int _val, Node* _next, Node* _random) {val _val;next _next;random _random;} }; */ class Solution { publi…

括号匹配问题(c和c++版本实现)

括号匹配问题 思路 遇见左括号入栈&#xff0c;遇见一个右括号弹出栈顶元素右括号入栈前如果栈已经为空&#xff0c;则不匹配如果不为空则读取并弹出&#xff0c;弹出来的元素与右括号做比较&#xff0c;必须匹配&#xff0c;不匹配返回false;如果最后栈里还有元素&#xff0c…

用队列实现栈 AND 用栈实现队列

用队列实现栈 思路 入队列就是入栈出队列的时候&#xff0c;就是把前面size-1个队列中的元素先出&#xff0c;这样最后一个元素就成队首元素了&#xff0c;再把出去的元素再次入队列读栈顶元素&#xff0c;过程和第二步是一样的&#xff0c;就是弹出后&#xff0c;再把它入队列…

最小栈的实现(设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。)

最小栈的实现 思路 两个栈&#xff0c;左边栈接受元素&#xff0c;右边栈存最小的元素入栈时&#xff0c;先入左边栈&#xff0c;随后进行比较&#xff0c;左边和右边栈顶元素进行比较&#xff0c;如果新元素小&#xff0c;就把新元素放在右边的栈顶位置&#xff0c;如果新元素…

再写循环队列----c++实现

再写循环队列 class MyCircularQueue { public:/** Initialize your data structure here. Set the size of the queue to be k. */MyCircularQueue(int k) {array (int *)malloc(sizeof(int)*k);capacity k;size 0;front 0;rear 0;}/** Insert an element into the circu…

再谈二叉树(二叉树概念,二叉树的性质,二叉树的存储结构)

树的概念 树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因 为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。它具有以下的特点&#xff1a;每个…

二叉树题目----1 前序中序后序遍历二叉树并返回相应的遍历(不是打印)

前序遍历 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*//*** Note: The returned array must be malloced, assume caller calls free().*/ int *array; int size;void _preorde…

二叉树题目----2 检查两颗树是否相同 和 对称二叉树的判定

检查两颗树是否相同 思路 根要相等 p->val q->val左子树相等 isSameTree(p->left,q->left)右子树也要相等 isSameTree(p->right,q->right) /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* …

二叉树题目---3 另一个树的子树 AND 二叉树最大深度

另一个树的子树 思路 两个数都遍历一遍&#xff0c;找到一个根结点相同时&#xff0c;判断以这个根结点为首的二叉树是否相等 前序遍历判断两棵树是否相同对于返回值的处理是难点 bool isSameTree(struct TreeNode *p, struct TreeNode *q) {if(p NULL && q NULL)…

二叉树题目----4 前序遍历重构二叉树 AND 求二叉树中所有结点的个数

前序遍历重构二叉树 思路 整个二叉树用数组存储因为先序遍历它先遍历根&#xff0c;再遍历左&#xff0c;左边没有跑完是不会去遍历右边的&#xff0c;所以遍历左子树&#xff0c;就是数组元素每回向后一个&#xff0c;个数-1遍历右边时&#xff0c;就是数组起始位置左子树跑到…

二叉树的进阶操作---(求二叉树中所有结点个数,求叶子结点个数,求第k层结点个数;在二叉树中查找某一结点;层序遍历;判断是否为完全二叉树)

typedef struct TreeNode {struct TreeNode *left;struct TreeNode *right;char val; }TreeNode;typedef struct Result{TreeNode * root; //构建的树的根结点int used; //构建过程中用掉的val个数 } Result;求二叉树中所有结点个数 void TreeSize(TreeNode *root, int …

c++中的智能指针详解(RAII, auto_ptr的原理及其改进,unique_ptr的原理,shared_ptr的原理,shared_ptr的缺陷及其改进)

为什么需要智能指针 代码中途退出&#xff0c;也能保证资源的合理释放&#xff0c;在c中没有垃圾回收机制的情况下&#xff0c;智能指针就可以保证我们申请的资源&#xff0c;最后忘记释放的问题&#xff0c;防止内存泄露&#xff0c;也帮我们减少了一定的负担&#xff0c;不用…