概要介绍
一般情况下,处理
socket通信
一个线程或者进程在处理读写的时候要么阻塞在那一直等要么非阻塞然后过会查看是否可读可写,这样会浪费大量的资源,假如需要对多个套接字进行处理读写那么得开很多个线程或者进程,IO复用技术
就是解决这个问题。本节详细讲解IO复用模型 select。
解决1024以下客户端时使用select是很合适的,但如果链接客户端过多,select采用的是轮询模型
,会大大降低服务器响应效率,不应在select上投入更多精力
2021-09-07
复习发现上代码的错误,直接的结构是if else{for} 应该修改为 if for。因为可能多个文件描述符产生动静,如果是一个sockfd 和 几个 客户端的fd,那么要同时处理新客户端的连接和旧客户端的读事件。第二个就是注意代码的注释,注意代码中两个if(–nready)的判断
文章目录
1 三种IO模型(重要)
举个常用的例子,去超市买货,
- 如果我们要的货物没到,那我们就在超市一直等,不做其他的事情这就是阻塞IO
- 我们每隔一段时间去超市看看货到了没有,这就是 非阻塞IO
- 这次我们学聪明了,让进货员货物到了就通知我去取,这就是多路IO复用
阻塞
-
在同步阻塞的IO模型中,在第一阶段“等待数据被写入Socket的缓冲区中”,操作系统会把当前的进程设置为阻塞状态,直到缓冲区被写入数据这个进程才被唤醒。这也就造成了一个问题,当操作系统把这个进程置为阻塞态的时候,这个进程就什么事都做不了了。
-
例如常见的
fork
多线程进行的read/write
非阻塞
- 为了解决“同步阻塞”进程可能无期限阻塞的情况,于是产生了“同步非阻塞”
在这种IO模型中,如果发现这个Socket里面没有准备好的数据就返回一个错误,而不是把这个进程设置为阻塞状态。也就是说我们可以轮询这个Socket查看有无准备好的数据。
造成的问题
- 假设此时我们的服务器需要管理很多的IO请求,如果给每一个IO都分配一个进程/线程,自旋的等待有无数据到来,无疑是很浪费资源的。
- 如果我们用一个进程,轮询所有的IO请求,又会使IO的响应变得很慢,所以引入多路io复用
多路复用
- 多路复用IO就是用一条线程,同时监听多个IO请求,并且在有IO请求产生的时候返回。注意,虽然我们的IO多路复用也会阻塞,但是这里的阻塞是应用层面的,也就是说在多路复用的方法上进行阻塞,而不是在操作系统层面去阻塞。
2 select模型函数使用
2.1 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,因为文件描述符从0开始计数 -
readfds
:需要监听的可读事件的文件描述符集合(如果有动静则该集合只会保留产生可读事件)看下面实例
-
writefds
:需要监听的可写事件的文件描述符集合 -
exceptfds
:需要监听的异常事件的文件描述符集合 -
timeout
:即告诉内核select等待多长时间之后就放弃等待。一般设为NULL 表示无动静就阻塞等待 -
返回值:
表示产生动静的文件描述符个数`
函数作用:
监听等待参数集合中的两类
文件描述符产生动静,产生动静后集合中只会剩下
产生动静的文件描述符
- sockfd:专门检查有没有客服端连接的监听套接字 ------数量 1
- connfd:已经连接成功的客服端通信套接字 ------数量 n
实例补充说明:
select(maxfd+1,&rset,NULL,NULL,NULL);
由于参数二放入集合,参数一填入数字,其余位置皆为NULL,
表示我们监听的是rset文件描述符集合
是否有可读事件(动静),如果有则该集合只会保留产生可读事件(动静)的文件描述符,否则就阻塞等待。
可读事件分为以下三种
- 新客服端连接(此时唯一
sockfd
放入集合) - 旧客服端发消息过来(此时对应的
connfd
放入集合) - 旧客户端断开连接(此时对应的
connfd
放入集合)
2.2 select()函数配套使用的四个宏
-
FD_ZERO(fd_set* set)
理解为初始化文件描述符几何 (既把所有位都设置为0) -
FD_SET(fd,fd_set* set)
理解为把文件描述符加入集合(本质为对应fd位设置为1) -
FD_CLR(fd,fd_set* set)
理解为把文件描述符从集合取出(本质为对应fd位设置为0) -
FD_ISSET(fd,fd_set* set)
理解为检测改文件描述符是否在集合(本质为对应的fd位是否设置为1)
3 多个客户端连接实现简单的服务器回射打印(附详细解释!!!)
客户端代码如下
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <signal.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#define SET_PORT 8000