socket编程(8)

本章目标:

  • 五种I/O模型
  • select
  • 用select改进回射客户端程序

 

一、五种I/O模型

  • 阻塞I/O
  • 非阻塞I/O
  • I/O复用 (select和poll)
  • 信号驱动I/O
  • 异步I/O

阻塞I/O

  

 非阻塞I/O

消耗cpu,一般很少使用。

 

 I/O复用

 

select管理文件描述符集合。

 

信号驱动I/O

是一种 拉  模式

 

异步I/O

是一种 推  模式

 

二、select

int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

参数:nfds为最大文件描述符+1

   中间三个参数为描述符集合,如果我们对某一个条件不感兴趣,可以把它设置为NULL;

注意:后面四个参数都是  输入输出参数。

void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

 

原理:

  select函数修改由指针readfds, writefds, excepfds, 所指向的描述符集合,因而这三个描述符集合是输入输出参数。

  调用该函数时,我们指定所关心的描述符的值,该函数返回时,结果将指示哪些描述符已经就绪。

  函数返回后,我们使用FD_ISSET宏来测试fd_set 集合中的描述符。描述符集合中任何与未就绪描述符对应的位返回时将清成 0 。

  为此,每次重新调用select函数时,我们都得再次把所有描述符集合内关心的位均置为1 。

  

  该函数返回时值表示跨所有描述符集合的已就绪的总位数。如果在任何描述符就绪之前定时器到时,将返回 0 ;返回 -1 表示出错。(比如中断等)。

 

总结一下:

select就像一个管理者

用select管理多个I/O

一旦其中的一个I/O或者多个I/O检测到我们所关心的事件,select函数返回,返回值为检测到的事件数、

并且返回哪些I/O发生了事件

遍历这些事件,进而处理事件。

 

三、用select改进回射客户端程序

代码:

void echo_cli(int sockfd)
{
    /*
    char sendbuf[1024]={0};
        char recvbuf[1024]={0};
        while(fgets(sendbuf,sizeof(sendbuf),stdin) != NULL)
        {
                
            writen(sockfd,sendbuf,strlen(sendbuf));

            int ret = readline(sockfd,recvbuf,sizeof(recvbuf));
            if(ret == 0)
            {
                 printf("client close\n");
                 break;
            }

            fputs(recvbuf,stdout);
            memset(sendbuf,0,sizeof(sendbuf));
            memset(recvbuf,0,sizeof(recvbuf));
        }
        close(sockfd);
*/
    fd_set rset;
    FD_ZERO(&rset);

    int nready;//检测到的事件个数
    int maxfd;
    /*//如果标准输入重定向,那么它可能不是0,所以这里用fileno球它的文件描述符*/
    int fd_stdin = fileno(stdin);
    if(fd_stdin > sockfd)
        
        maxfd = fd_stdin;
    else 
        maxfd = sockfd;

        char sendbuf[1024]={0};
        char recvbuf[1024]={0};

    while(1)
    {
        /*因为集合中的数据是会发生改变的,所以不可以把下面两句放到循环外*/
        FD_SET(fd_stdin,&rset);
        FD_SET(sockfd,&rset);

        nready = select(maxfd+1,&rset,NULL,NULL,NULL);
        if(nready == -1)//出错
            ERR_EXIT("select");
        if(nready == 0)//出错
            continue;
        if(FD_ISSET(sockfd,&rset)) //套接口产生了可读
        {
            /*读取*/
            int ret = readline(sockfd,recvbuf,sizeof(recvbuf));
            if(ret == 0)
            {
                 printf("server close\n");
                 break;
            }

            fputs(recvbuf,stdout);
            memset(recvbuf,0,sizeof(recvbuf));
        }
        if(FD_ISSET(fd_stdin,&rset))  //标准输入有数据
        {
            if(fgets(sendbuf,sizeof(sendbuf),stdin)==NULL)
                break;
            writen(sockfd,sendbuf,strlen(sendbuf));
            memset(sendbuf,0,sizeof(sendbuf));
        }
    }
    close(sockfd);
}

没有用select之前,先断开服务器端的(服务器端close),那么网络状态为:

由于客户端阻塞在fgets(),导致进行不到 read函数。所以状态无法向后进行。

 

使用了select之后,网络状态为

 

posted @ 2017-03-28 09:33  ren_zhg1992  阅读(186)  评论(0)    收藏  举报