day08
本章目标
- 五种
I/O
模型 select
- 用
select
改进回射客户端程序
五种I/O模型
- 阻塞
I/O
- 非阻塞
I/O
I/O
复用(select
和poll
)- 信号驱动
I/O
- 异步
I/O
1、阻塞I/O模型
阻塞等待数据到来。
2、非阻塞I/O模型
**
忙等待
**,用的很少,程序一直在等待数据,没有数据相当于死循环,占用CPU资源。
fcntl(fd, F_SETFL, flag|O_NONBLOCK);
详情查看本博客C语言部分:跳转连接
PS:
EMOULDBLOCK
错误应当改为EWOULDBLOCK
1 | NAME |
3、I/O复用
通过
select
来实现,通过select
管理多个文件描述符。一旦由文件描述符监测到数据到来select
就返回,然后调用recv
函数对数据处理,
4、信号驱动I/O
不常用,非阻塞模式,有消息到来通过信号跳到消息处理函数,没有消息,处理其他事情。
5、异步I/O
效率最高的处理方式
。aio_read
函数,有一个缓冲区,如果没有数据立即返回,有数据的话,把数据拷贝到应用层的缓冲区。复制完成后,通过信号通知应用层的数据。与第四种模型相似,但是又有很大区别。
select模型 —>重点
man 2 select
,学会使用帮助手册。
select
:用来管理fd
,地位:管理者,管理多个I/O
,一旦其中的一个I/O
监测到所感兴趣的事件,那么select
函数返回,返回值为监测到事件的个数。并且返回那些I/O
发生了那些事件。遍历事件然后进行处理,单进程处理比较好。
1 | #include <sys/select.h> |
select
的参数说明:int nfds
:存放到读、写、异常的集合文件描述符的最大值+1
。就是改参数的值。fd_set *readfds
:读的集合,如果有数据可读的套接口放在此集合,放的为文件描述符。输入输出型参数。fd_set *writefds
:写的集合,如果有数据可写的套接口放在此集合,放的为文件描述符。输入输出型参数。fd_set *exceptfds
:异常的集合输入输出型参数。struct timeval *timeout
:指定超时时间,NULL
不设置超时时间。设置时间后如果没有监测到时间,也会返回返回值为0
。输入输出型参数。select
函数每次返回一次,都需要调用FD_ZERO
和FD_SET
,因为第二、三、四个参数都是输入输出型参数,返回后其值会发生变化,需要重新设置内容。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48fd_set rset;
FD_ZERO(&rset);
int maxfd;
int nready;
char sendbuf[1024] = {0};
char recvbuf[1024] = {0};
int fd_stdin = fileno(stdin);//为了防止标准输入和输出被重定向所以用fileno转换stdin/stdout为fd
if(fd_stdin > sock)//设置最大fd
maxfd = fd_stdin;
else
maxfd = sock;
while(1)
{
FD_SET(fd_stdin, &rset);//每次都需要添加在while循环,详情查看博客原因
FD_SET(sock, &rset);
nready = select(maxfd + 1, &rset, NULL, NULL, NULL);
if(nready == -1)
{
ERR_EXIT("select");
}
else if(nready == 0)
continue;
if(FD_ISSET(sock, &rset))//判断是那个信号的,调其处理的函数
{
int ret = readline(sock, recvbuf, sizeof(recvbuf));
if(ret == -1)
{
ERR_EXIT("readline");
}
else if(ret == 0)
{
printf("server close\n");
break;
}
fputs(recvbuf, stdout);
memset(recvbuf, 0, sizeof(recvbuf));
}
else if(FD_ISSET(fd_stdin, &rset))
{
if(fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)
break;
writen(sock, sendbuf, strlen(sendbuf));
memset(sendbuf, 0, sizeof(sendbuf));
}
}
close(sock);
return 0;
timeout
的结构体:
1 | The timeout |
select
的返回值:
1 | RETURN VALUE |
四个宏
void FD_CLR(int fd, fd_set *set);
如果
fd
存在于集合中,将fd
从集合中移除。int FD_ISSET(int fd, fd_set *set);
判定
fd
是否再集合中,不会改变集合的内容。void FD_SET(int fd, fd_set *set);
将
fd
添加到集合当中void FD_ZERO(fd_set *set);
将集合清空,即删除集合内所有的
fd