TCP/IP网络编程之I/O复用select函数

2019-07-15 20:23:46 浏览数 (2)

之前我们使用了几种服务器模型,一个是单进程的, 同一时刻只能给一个客户端提供服务, 后来我们使用了多进程, 每个客户端fork新进程进行请求处理

IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程,可以使用一个进程服务多个客户端.

多进程服务模型:
I/O 复用进程模型:

select 实现I/O 复用

select实现比较简单,主要使用select函数 函数原型:

代码语言:javascript复制
#include <sys/select.h>
#include <sys/time.h>

int select(int maxfd, fd_set * readset, fd_set * writeset, fd_set * exceptset, const struct timeval * timeout);

成功时返回大于0的值, 失败时返回 -1

参数解释:

代码语言:javascript复制
maxfd 监视对象文件描述符的数量,举例写法 sever_sock 1
readset 存储的待读取数据文件描述符
writeset 可传输无阻塞数据文件描述符 
exceptset 发生异常的文件描述符
timeout 超时设置

select函数返回值, 如果返回大于0的整数, 说明相应数量的文件描述符发生了变化.

文件描述符管理

我们发现在 参数类型上有 fd_set类型,这是什么类型呢? fd_set结构是文件描述符对应的位存储数据格式, 当我们管理这些监控的文件描述符时, 可以以下宏来实现

代码语言:javascript复制
FD_ZERO(fd_set * fdset) 
将 fd_set 变量的所有位初始化位0

FD_SET(int fd, fd_set * fdset)
在参数 fd_set 指向的变量中注册文件描述符 fd 的信息

FD_CLR(int fd, fd_set * fdset)
从参数 fdset 指向的变量中清除文件描述符 fd 的信息

FD_ISSET(int fd, fd_set * fdset) 
若参数 fdset  指向的变量中包含文件描述符 fd 的信息,则返回真
timeval 超时设置结构体
代码语言:javascript复制
struct timeval 
{
    long tv_sec;  //seconds
    long tv_usec; //microseconds
}
select函数执行前后示例图
示例代码(回声)

select.c

代码语言:javascript复制
#include <stdio.h>
#include <sys/select.h>

#define BUF_SIZE 1024

int main(int argc, char *argv[])
{
    //监视的文件描述符
    fd_set reads, temps;
    struct timeval timeout;
    int result, str_len;
    char buf[BUF_SIZE];

    FD_ZERO(&reads);
    FD_SET(0, &reads); //0 is standard input(console)

    while (1)
    {
        //因为每次select会重置监控句柄,所以赋值给临时
        temps = reads;

        timeout.tv_sec = 5;
        timeout.tv_usec = 0;

        // puts 只有事件发生或者发生超时才执行,否则select阻塞
        puts("xxxx");
        result = select(1, &temps, 0, 0, &timeout);

        if (result == -1)
        {
            puts("select() error");
        }
        else if (result == 0)
        {
            puts("nothing event change..time out");
        }
        else
        {
            if (FD_ISSET(0, &temps))
            {
                str_len = read(0, buf, BUF_SIZE);
                printf("message from consle: %sn", buf);
            }
        }
    }
}

执行输出

代码语言:javascript复制
gcc select.c -o select

./select
xxxx
123456
message from consle: 123456

xxxx
7890ha
message from consle: 7890ha

xxxx
nothing event change..time out
xxxx
777
message from consle: 777

xxxx
^C

0 人点赞