兰州网站建设价格谁有学做网站论坛账号
news/
2025/9/22 21:51:10/
文章来源:
兰州网站建设价格,谁有学做网站论坛账号,wordpress编辑器经典,深圳网站开发搜行者seoIO复用 概述IO模型阻塞式IO非阻塞式IOIO复用select、poll、epoll异同 信号驱动式IO异步IO select函数select示例代码 poll函数poll示例代码 epoll函数创建 epoll_create注册、修改、删除 epoll_ctl轮询 I/O 事件的发生 epoll_waitepoll示例代码 基于TCP和epoll在线多人… IO复用 概述IO模型阻塞式IO非阻塞式IOIO复用select、poll、epoll异同 信号驱动式IO异步IO select函数select示例代码 poll函数poll示例代码 epoll函数创建 epoll_create注册、修改、删除 epoll_ctl轮询 I/O 事件的发生 epoll_waitepoll示例代码 基于TCP和epoll在线多人聊天室服务器例子 概述 什么是IO复用呢I/O复用(I/O multiplexing)指的是通过一个支持同时感知多个描述符的函数系统调用阻塞在这个系统调用上等待某一个或者几个描述符准备就绪就返回可读条件。 IO多路复用解决了什么问题呢当多个客户端与服务器通信时若服务器阻塞在其中一个客户的read(sockfd1,…)当另一个客户数据到达sockfd2时服务器无法及时处理此时需要用到IO多路复用。即同时监听n个客户当其中有一个发来消息时就从select的阻塞中返回然后调用read读取收到消息的sockfd然后又循环回select阻塞。这样就解决了阻塞在一个消息而无法处理其它的。即用来解决对多个I/O监听时,一个I/O阻塞影响其他I/O的问题。 这时候大家可能会想到线程和进程也能做到这样的效果啊(基于tcp多线程在线聊天室例子)但是CPU切换进程和线程的成本是很高的这也正是IO复用的优点可以做到用更少的资源完成更多的事。 IO模型 阻塞式IO 这种类型的IO会阻塞等待到有输入。这类IO大部分时间处于睡眠态、阻塞态、挂起态。优点是不占用 CPU 宝贵的时间片但是同一时刻只能处理一个操作、效率比较低。 如何理解呢就像你在家里睡觉等着你的外卖送到送到了给你打电话你才起床去拿外卖。如果一次性到了很多外卖你也只能一个电话一个电话去接听然后挨着拿。 应用场景多为多进程、多线程的TCP 并发服务器、客户端。 非阻塞式IO 该种类型的IO需要程序轮询检测输入如果有输入就调用IO进行读取数据。优点是提高了程序的执行效率但是需要占用更多的 CPU 和系统资源使得 CPU 负荷增高。 我们还举外卖的例子。这种情况就像你非常饿一直顶着配送地图一分钟刷新一次地图看看到了没到了你就立即下楼去拿拿完了继续刷新地图看看别的还有多久到。 IO复用 这类IO采用阻塞式IO组长机制。多路IO共用一个同步阻塞接口任意IO可操作都可激活IO操作。它能同时等待多个文件描述符而这些文件描述符其中的任意一个进入读就绪状态函数就可以返回。 IO复用包含了select、poll和epoll。其中select相当于你家楼下放外卖的桌子你得自己挨个外卖看一遍直达找到你的外卖。而epoll像外卖柜你只要看一下手机就知道你的外卖在哪个柜子里面。 select、poll、epoll异同 相同之处 1 . 都可以用于监听多个文件描述符的读写事件。 2 . 都是阻塞式的即当没有任何事件发生时它们都会一直阻塞等待。 3 . 都可以通过设置超时时间来控制阻塞等待的时间。 不同之处 1 . select和poll使用轮询的方式来检查所有的文件描述符而epoll使用回调的方式只有在有事件发生时才会调用回调函数。 2 . select和poll的文件描述符集合是通过参数传递给函数的而epoll使用epoll_ctl函数来注册和删除文件描述符通过epoll_wait函数来等待事件。 3 . select和poll对于大量的文件描述符来说性能会下降因为每次都需要遍历整个文件描述符集合而epoll使用红黑树来存储文件描述符效率更高。 4 . select和poll支持的文件描述符数量有一定的限制而epoll没有明确的限制。 总的来说epoll相对于select和poll来说在处理大量的并发连接时性能更好并且使用更方便。 信号驱动式IO 注册一个IO信号事件在数据可操作时通过SIGIO信号通知线程这应该算是一种异步机制. 异步IO 应用进程通知内核开始一个异步I/O操作并让内核在整个操作包含将数据从内核复制到应该进程的缓冲区完成后通知应用进程。例如POSIX 的IO _系列函数。 select函数 头文件 #include sys/select.h #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 返回值返回值是一个整数表示有多少个文件描述符已经就绪。如果返回0表示在指定的超时时间内没有任何文件描述符就绪如果返回-1表示发生错误。 //FD_CLR(inr fd,fd_set* set)用来清除描述词组 set 中相关 fd 的位
//FD_ISSET(int fd,fd_set *set)用来测试描述词组 set 中相关 fd 的位是否为真(遍历检测函数)
//FD_SETint fd,fd_set*set用来设置描述词组 set 中相关 fd 的位
//FD_ZEROfd_set *set用来清除描述词组 set 的全部位(在初始化时用到以免里面有垃圾值)fd_set readfds;FD_ZERO(readfds);//清空FD_SET(socketfd,readfds); //用来设置描述词组 set 中相关 socketfd-fd 的位fd_set changeReadfds readfds;int count select(nfds,useChangeReadfds, NULL,NULL,0);select示例代码
#include stdio.h
#include stdlib.h
#include sys/types.h
#include sys/stat.h
#include sys/wait.h
#include string.h
#include fcntl.h
#include unistd.h
#include errno.h
#include sys/socket.h
#include arpa/inet.h
#include time.h
#include sys/select.h
typedef struct sockaddr SA;
typedef struct sockaddr_in SIN;
#define MAXBACKLOG 100int Socket(int domain,int type,int protocol);
int Bind(int sockfd,struct sockaddr * my_addr,int addrlen);
int Listen(int s,int backlog);
int Accept(int s,struct sockaddr * addr,int * addrlen);
void * clientRecvDataFunction(void * arg);//运行格式 ./app 192.168.5.166 8888
int main(int argc,char *argv[])
{ int opt 1;//建立监听套接字int socketfd Socket(AF_INET,SOCK_STREAM,0);//需要进行重用地址及其端口号setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,opt,sizeof(opt));//绑定信息编写服务器信息SIN serverinfo;serverinfo.sin_family AF_INET; //协议IPV4serverinfo.sin_port htons(atoi(argv[2])); //网络字节序(大端字节序)与主机字节序(小端字节序) serverinfo.sin_addr.s_addr inet_addr(argv[1]);int addrlen sizeof(SIN);Bind(socketfd,(SA*)serverinfo,addrlen);//监听Listen(socketfd,MAXBACKLOG);//select函数参数填写int nfds socketfd1;fd_set readfds; //栈区定义 先清空 再写入位FD_ZERO(readfds); //清空FD_SET(socketfd,readfds); //用来设置描述词组set中相关socketfd-fd的位//读写while(1){fd_set useChangeReadfds readfds;int count select(nfds,useChangeReadfds, NULL,NULL,0);if(count 0){//文件描述符socketfd调用acceptif(FD_ISSET(socketfd,useChangeReadfds)){SIN clientinfo;int clientaddrlen sizeof(SA);int newfd Accept(socketfd,(SA*)clientinfo,clientaddrlen);printf(客户端地址:%s 端口号:%d\n,inet_ntoa(clientinfo.sin_addr),ntohs(clientinfo.sin_port));//将新的文件描述符添加至readfdsFD_SET(newfd,readfds);//调整检测范围newfd nfds?(nfds):(nfds nfds);}//文件描述符非socketfd调用readelse{for(int startFd socketfd1; startFd nfds;startFd){if(FD_ISSET(startFd,useChangeReadfds)){//读取客户端发送来的数据char readbuff[512]{0};int len read(startFd,readbuff,sizeof(readbuff));if(len 0){printf(%d:%s\n,startFd,readbuff);}else if(len 0){printf(%d:客户端退出\n,startFd);FD_CLR(startFd,readfds);//存在弊端nfds不好调整close(startFd);}else if(len 0){printf(%d:客户端异常退出\n,startFd);FD_CLR(startFd,readfds);//存在弊端nfds不好调整close(startFd);}}}}}}//关闭close(socketfd);return 0;
}
int Socket(int domain,int type,int protocol)
{int socketFd socket(domain,type,protocol);if(socketFd -1){perror(socket);exit(1);}return socketFd;
}
int Bind(int sockfd,struct sockaddr * my_addr,int addrlen)
{int val bind(sockfd,my_addr,addrlen);if(val){perror(bind);exit(1);}return 0;
}
int Listen(int s,int backlog)
{int val listen(s,backlog);if(val -1){perror(listen);exit(1);}return val;
}
int Accept(int s,struct sockaddr * addr,int * addrlen)
{int NEWfd accept(s,addr,addrlen);if(NEWfd -1){perror(listen);exit(1);}return NEWfd;
}poll函数 头文件 #include poll.h 函数原型int poll(struct pollfd fd[], nfds_t nfds, int timeout); 参数介绍 fd[]文件描述符结构体。 nfds指定结构体数组元素个数。 timeout 返回值成功时 poll() 返回结构体中 revents 域不为 0 的文件描述符个数如果在超时前没有任何事件发生poll()返回 0。 //struct pollfd结构体内容
struct pollfd
{int fd; //文件描述符 所要检测的文件描述符short events; //请求的事件 检测文件描述符的事件short revents; //返回的事件内核给的反馈
};struct pollfd fds[1025];//清空结构体数组//清空结构体数组for(int i 0;i sizeof(fds)/sizeof(struct pollfd);i){fds[i].fd -1;}//初始化填入 socketfd;fds[0].fd socketfd; //文件描述符fds[0].events POLLIN; //读事件//nfds 用来指定第一个参数数组元素个数int nfds 1;int count poll(fds,nfds, -1);poll示例代码
#include stdio.h
#include stdlib.h
#include sys/types.h
#include sys/stat.h
#include sys/wait.h
#include string.h
#include fcntl.h
#include unistd.h
#include errno.h
#include sys/socket.h
#include arpa/inet.h
#include time.h
#include poll.h
typedef struct sockaddr SA;
typedef struct sockaddr_in SIN;
#define MAXBACKLOG 100int Socket(int domain,int type,int protocol);
int Bind(int sockfd,struct sockaddr * my_addr,int addrlen);
int Listen(int s,int backlog);
int Accept(int s,struct sockaddr * addr,int * addrlen);
void * clientRecvDataFunction(void * arg);//./app 192.168.5.166 8888
int main(int argc,char *argv[])
{ int opt 1;//建立监听套接字int socketfd Socket(AF_INET,SOCK_STREAM,0);//需要进行重用地址及其端口号setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,opt,sizeof(opt));//绑定信息编写服务器信息SIN serverinfo;serverinfo.sin_family AF_INET; //协议IPV4serverinfo.sin_port htons(atoi(argv[2]));serverinfo.sin_addr.s_addr inet_addr(argv[1]);int addrlen sizeof(SIN);Bind(socketfd,(SA*)serverinfo,addrlen);//监听Listen(socketfd,MAXBACKLOG);//pollt函数参数填写struct pollfd fds[1025];//清空结构体数组for(int i 0;i sizeof(fds)/sizeof(struct pollfd);i){fds[i].fd -1;}//初始化填入socketfd;fds[0].fd socketfd; //文件描述符fds[0].events POLLIN; //读事件//nfds用来指定第一个参数数组元素个数int nfds 1;//读写while(1){int count poll(fds,nfds, -1);if(count 0){//检查fds[0]的fd是否发生动作------acceptif(fds[0].revents POLLIN){SIN clientinfo;int clientaddrlen sizeof(SA);int newfd Accept(socketfd,(SA*)clientinfo,clientaddrlen);printf(客户端地址:%s 端口号:%d\n,inet_ntoa(clientinfo.sin_addr),ntohs(clientinfo.sin_port));//需要将newfd放至fds中for(int i 0; i sizeof(fds)/sizeof(struct pollfd);i){if(fds[i].fd -1){fds[i].fd newfd; //文件描述符fds[i].events POLLIN; //读事件i nfds?(nfds):(nfds nfds); //nfds与下标有联系break;}}}else{//检查fds[0]的是否发送动作--------readfor(int startfd 1;startfdnfds;startfd){if(fds[startfd].revents POLLIN){char readbuff[512]{0};int len read(fds[startfd].fd,readbuff,sizeof(readbuff));if(len 0){printf(%d:%s\n,fds[startfd].fd,readbuff);}else if(len 0){printf(%d:客户端退出\n,fds[startfd].fd);//归位处理close(fds[startfd].fd);fds[startfd].fd-1;}else if(len 0){printf(%d:客户端异常退出\n,fds[startfd].fd);//归位处理close(fds[startfd].fd);fds[startfd].fd-1;}}}}}}//关闭close(socketfd);return 0;
}
//下面的函数与select相同epoll函数
创建 epoll_create 头文件 #include sys/epoll.h 函数原型int epoll_create(int size) 参数介绍 size标识这个监听的数目最大有多大。 返回值 成功时这些系统调用将返回非负文件描述符。如果出错则返回-1并且将errno设置为指示错误。 int epollfd epoll_create(1024);注册、修改、删除 epoll_ctl 头文件 #include sys/epoll.h 函数原型int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 参数介绍 epfd epoll 专用的文件描述符。 op要进行的操作EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修改、EPOLL_CTL_DEL 删除。 fd关联的文件描述符。 event指向 epoll_event 的指针。 返回值成功返回1失败返回0. //结构体 epoll_event 被用于注册所感兴趣的事件和回传所发生待处理的事件定义如下
typedef union epoll_data_t { //联合体void *ptr;int fd; //比较常用__uint32_t u32;__uint64_t u64;
} epoll_data_t;//保存触发事件的某个文件描述符相关的数据struct epoll_event {__uint32_t events; /* epoll event */epoll_data_t data; /* User data variable */
};
/*其中 events 表示感兴趣的事件和被触发的事件可能的取值为
EPOLLIN表示对应的文件描述符可以读
EPOLLOUT表示对应的文件描述符可以写
EPOLLPRI表示对应的文件描述符有紧急的数可读
EPOLLERR表示对应的文件描述符发生错误
EPOLLHUP表示对应的文件描述符被挂断
EPOLLET ET 的 epoll 工作模式*/struct epoll_event event;event.events EPOLLIN; //事件成员event.data.fd socketfd; //数据epoll_ctl(epollfd,EPOLL_CTL_ADD,socketfd, event);轮询 I/O 事件的发生 epoll_wait 头文件 #include sys/epoll.h 函数原型int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout); 参数介绍 epfdepoll 专用的文件描述符。 events回传处理事件的数组。 maxevents每次能处理的事件数。 timeout等待 I/O 事件发生的超时值。 返回值返回发生的事件数失败返回-1。 int count epoll_wait(epollfd,events,10,-1);epoll示例代码
#include stdio.h
#include stdlib.h
#include sys/types.h
#include sys/stat.h
#include sys/wait.h
#include string.h
#include fcntl.h
#include unistd.h
#include errno.h
#include sys/socket.h
#include arpa/inet.h
#include time.h
#include sys/epoll.htypedef struct sockaddr SA;
typedef struct sockaddr_in SIN;
#define MAXBACKLOG 100int Socket(int domain,int type,int protocol);
int Bind(int sockfd,struct sockaddr * my_addr,int addrlen);
int Listen(int s,int backlog);
int Accept(int s,struct sockaddr * addr,int * addrlen);
void * clientRecvDataFunction(void * arg);//./app 192.168.5.166 8888
int main(int argc,char *argv[])
{ int opt 1;//建立监听套接字int socketfd Socket(AF_INET,SOCK_STREAM,0);//需要进行重用地址及其端口号setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,opt,sizeof(opt));//绑定信息编写服务器信息SIN serverinfo;serverinfo.sin_family AF_INET; //协议IPV4serverinfo.sin_port htons(atoi(argv[2]));serverinfo.sin_addr.s_addr inet_addr(argv[1]);int addrlen sizeof(SIN);Bind(socketfd,(SA*)serverinfo,addrlen);//监听Listen(socketfd,MAXBACKLOG);//epollt创建根节点int epollfd epoll_create(1024);//添加socketfd文件描述符至内核 红黑树struct epoll_event event;event.events EPOLLIN; //事件成员event.data.fd socketfd; //数据epoll_ctl(epollfd,EPOLL_CTL_ADD,socketfd, event);//读写while(1){struct epoll_event events[10];int count epoll_wait(epollfd,events,10,-1);if(count 0){for(int i 0; i count;i){if(events[i].events EPOLLIN){if(events[i].data.fdsocketfd){//等待连接SIN clientinfo;struct epoll_event event;int clientaddrlen sizeof(SA);int newfd Accept(socketfd,(SA*)clientinfo,clientaddrlen);printf(客户端地址:%s 端口号:%d\n,inet_ntoa(clientinfo.sin_addr),ntohs(clientinfo.sin_port));//需要将newfd放至红黑树中event.events EPOLLIN; //事件成员event.data.fd newfd; //数据epoll_ctl(epollfd,EPOLL_CTL_ADD,newfd, event);}else{//readchar readbuff[512]{0};int len read(events[i].data.fd,readbuff,sizeof(readbuff));if(len 0){printf(%d:%s\n,events[i].data.fd,readbuff);}else if(len 0){printf(%d:客户端退出\n,events[i].data.fd);epoll_ctl(epollfd,EPOLL_CTL_DEL,events[i].data.fd,NULL);close(events[i].data.fd);}else if(len 0){printf(%d:客户端异常退出\n,events[i].data.fd);epoll_ctl(epollfd,EPOLL_CTL_DEL,events[i].data.fd,NULL);close(events[i].data.fd);}}}}}}//关闭close(socketfd);return 0;
}
//下面的函数与select相同基于TCP和epoll在线多人聊天室服务器例子
点我查看
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/910493.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!