一、select模型特点
select是传统的IO多路复用模型,与其他IO多路复用模型(如epoll,kqueue)不同的是,select函数能够同时监听多个socket句柄的可读、可写和异常等事件。当某个socket句柄上的事件发生变化时,select函数就会返回,并将所有发生变化的socket句柄集合返回给用户程序,用户程序可以通过遍历这个集合来逐个处理socket事件。
select模型具有较好的兼容性,在大多数操作系统和编程语言中都有实现。同时,由于是同步阻塞模型,所以在并发访问不太密集的场景下,具有较好的稳定性。
但是,由于select模型每次只能监听1024个socket句柄(Windows下更少),因此在高并发访问的场景下,不太适合使用。
二、在IO多路复用模型中使用select
在IO多路复用模型中,使用select函数来监听多个socket句柄的事件,等待有事件发生时处理这些事件。
首先需要用fd_set数据类型来表示要监听的socket集合,通常只需要用到其中的“读就绪”信号。通过调用select函数,并传入要监听的socket集合(也可以分成多个集合)以及超时时间,等待其中任何一个socket句柄的事件发生。
当select函数返回时,需要遍历返回的socket集合,并逐个处理事件,比如接收客户端连接请求、读取数据、写入数据等操作。
三、select模型的作用
select模型的主要作用是实现并发访问和事件监听,并且既可以用于客户端,也可以用于服务器端。
在服务器端,select模型可以监听多个客户端的请求,并响应这些请求。通过即时地处理请求,可以大大提高服务器端的响应能力和并发处理能力,实现高性能的服务器端应用。
在客户端,select模型可以实现同时请求多个资源,提高访问效率,减少用户等待时间。
四、select模型的应用场景
由于select模型在IO多路复用模型中最古老、最基础,因此在某些场景下,仍然可能是最佳选择。
比如在简单的网络应用中,select模型可以通过监听多个连接,实现并发访问和快速响应。在一些低流量场景下,由于没有高并发访问,因此可以保证稳定性。
同时,在学习网络编程时,也可以先从select模型开始,了解IO多路复用模型的基础知识。
五、select模型服务器
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/time.h> #define BUF_SIZE 100 int main(int argc, char *argv[]) { int serv_sock, clnt_sock; struct sockaddr_in serv_adr, clnt_adr; socklen_t adr_sz; int fd_max, str_len, fd_num, i; char buf[BUF_SIZE]; fd_set reads, cpy_reads; struct timeval timeout; if(argc!=2) { printf("Usage : %s \n", argv[0]); exit(1); } serv_sock=socket(PF_INET, SOCK_STREAM, 0); memset(&serv_adr, 0, sizeof(serv_adr)); serv_adr.sin_family=AF_INET; serv_adr.sin_addr.s_addr=htonl(INADDR_ANY); serv_adr.sin_port=htons(atoi(argv[1])); if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1) exit(1); if(listen(serv_sock, 5)==-1) exit(1); FD_ZERO(&reads); FD_SET(serv_sock, &reads); fd_max=serv_sock; while(1) { cpy_reads=reads; timeout.tv_sec=5; timeout.tv_usec=5000; if((fd_num=select(fd_max+1, &cpy_reads, 0, 0, &timeout))==-1) break; if(fd_num==0) continue; for(i=0; i<fd_max+1; i++) { if(FD_ISSET(i, &cpy_reads)) { if(i==serv_sock) // connection request! { adr_sz=sizeof(clnt_adr); clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz); FD_SET(clnt_sock, &reads); if(fd_max<clnt_sock) fd_max=clnt_sock; printf("connected client: %d \n", clnt_sock); } else // read message! { str_len=read(i, buf, BUF_SIZE); if(str_len==0) // close request! { FD_CLR(i, &reads); close(i); printf("closed client: %d \n", i); } else { write(i, buf, str_len); // echo! } } } } } close(serv_sock); return 0; }
六、select模型工作原理
select模型基于IO多路复用机制,使用select函数来监听多个socket句柄的事件,等待有事件发生时处理这些事件。当有事件发生时,select函数就会返回,并将所有发生变化的socket句柄集合返回给用户程序,用户程序可以通过遍历这个集合来逐个处理socket事件。
在实现过程中,需要用fd_set数据类型来表示要监听的socket集合,通过调用select函数,并传入要监听的socket集合以及超时时间,等待其中任何一个socket句柄的事件发生。当select函数返回时,需要遍历返回的socket集合,并逐个处理事件。
七、我们所熟知的网络IO模型select
在网络编程中,select模型是最早、也是最为流行的网络IO模型之一。
select模型的核心思想是使用select函数来监听多个socket句柄,等待它们的读写ready事件,从而实现并发收发数据,提高网络传输效率。
由于其简单易懂、兼容性强、用途广泛,因此仍然被广泛应用在各类网络应用中。
八、select模型的编程步骤
使用select模型进行网络编程的步骤如下:
1. 创建socket。
2. 初始化(绑定)socket地址。
3. 调用listen函数,将socket设置为监听状态,等待客户端连接请求。
4. 创建fd_set类型的变量,用于存储要监听的socket集合。
5. 将要监听的socket加入到fd_set类型变量中。
6. 调用select函数,等待socket事件发生,返回有事件发生的socket集合。
7. 遍历socket集合,处理所有事件。
8. 关闭socket。
九、select模型能突破1024吗?
select模型最多只能监听1024个socket句柄,无法处理更大的并发访问量。为了解决这个问题,后来出现了更先进的IO多路复用模型,比如epoll和kqueue,它们能够监听更多的socket句柄。
虽然select模型的并发能力相对较弱,但在某些场景下仍然有其独特的优势,比如在低流量、低并发的网络应用中。
结语
以上是对select模型的详细阐述,希望能够对读者理解IO多路复用模型、网络编程等方面有所启发。同时,本文也为读者提供了一个基于select模型的简单服务器端代码示例。
原创文章,作者:QROTG,如若转载,请注明出处:https://www.506064.com/n/371885.html