模型一:阻塞模型---进程效率低;CPU利用低
模型二:非阻塞模型---进程效率高;但是CPU利用率低;
模型三:复用I/O模型---CPU利用率提高
思想:对于任何一个套接字描述符发生事件时才由系统去唤醒进程,从而不需要因轮询而占用CPU;
对于I/O复用典型的应用如下:
(1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。
(2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
(3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。
(4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。
(5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。
函数:select
int select(int nfds, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout);
函数是用来对文件描述符集监视的;只要集合中有文件描述符发生事件了,表示就绪了;
fd_set *readset, fd_set *writeset, fd_set *exceptset表示该函数监视的事件类型;
struct timeval *timeout表示来看事件发生前如何操作,是一直等待(NULL),不等待直接返回(0);还是等待一定时间后返回(大于0的值)
int nfds表示用来设置select检查文件描述符的最大值;故而他检测的值范围是0~nfds-1。
返回值就是发生事件的个数,如果没有返回0;如果错误则返回-1;
一个进程启动时,都会打开3个文件:标准输入、标准输出和标准出错处理。这3个文件分别对应文件描述符为0、1和2(宏替换STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO)
效果:此函数让对事件发生过程交给了内核去处理;不像前面阻塞模式中,对于每个套接字描述符都得去等待而降低进程效率;也不像非阻塞中,对于每个套接字描述符都得不断轮询扫描而达到是否有事件发生,而降低CPU利用率。本函数将多个合一的效果,多个套接字事件发生效果,集中到一个套接字集合中,只要有则就可以焕发进程处理。
实例:
#include "server.h" int main() { int listen_fd, connect_fd, max_fd; struct sockaddr_in serveraddr, clientaddr; char buf[MAXBUF]; int length; fd_set rdfs, tempfs; int num; listen_fd = socket(AF_INET, SOCK_STREAM, 0); memset(&serveraddr, 0, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(8000); serveraddr.sin_addr.s_addr = inet_addr("192.168.0.104"); bind(listen_fd, (SA *)&serveraddr, sizeof(serveraddr)); listen(listen_fd, 10); //connect_fd = bind(); memset(buf, '