select函数详解及其在I|O复用模型中的应用

来源:互联网 发布:bae 数据库 编辑:程序博客网 时间:2024/06/04 05:43

一.select函数详解

#include <sys/select.h>

#include <sys/time.h>

int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *excptset, const strcut timeval *timeout)

返回:准有就绪的文件描述数量,若超时则返回0,否者返回-1


针对select中最后一个时间参数情况如下

struct timeval{    long tv_sec;//seconds    long tv_usec;//microseconds};

1.timeout = NULL

表示将会一直等待下去,并且堵塞。也有可能系统捕获到了异常信号,强制返回-1.

2.time->tv_sec != 0 && timeout->tv_usec != 0

timeout作为计时器,进行倒计时,如果超时了那么,select函数就会返回0。也有可能系统捕获到了异常信号,强制返回-1.

3.time->tv_sec == 0 && timeout->tv_usec == 0

将会以轮训的方式等待下,还没有准备就绪的描述符。


针对select返回值详细分析

1.负值

系统捕捉到信号,进入相应的信号处理函数。

2.0

等待超时

3.正值

表示有多少个描述符号准备就绪


fd_set的宏操作函数

FD_ZERO(fd_set *fdset) //clear all bit in fdset

FD_SET(int fd, fd_set *fdset) //turn on the bit for fd on fdset

FD_ISSET(int fd, fd_set *fdset) // is on the bit for fd on fdset ?

FD_CLR(int fd, fd_set *fd_set) //turn offthe bit for fd on fdset

在sys/select函数中规定,FD_SETSIZE的值表示描述符总数,一般值默认为1024。但是在不同64位中的值可能不同。


二.select函数的应用

针对客服端一方,我们设计了一个select在批量输入中的一个模型。但是该明显存在一个大量的陷阱,如下分析所示

#include "unp"void doClient_echo(FILE *fp, int sockfd){    fd_set rset;    int maxfdp;    FD_ZERO(&rset);    char recvbuf[MAXLINE], sendbuf[MAXLINE];    for(;;){        FD_SET(fileno(fp), &rset);        FD_SET(sockfd, &rset);        maxfdp = fileno(fp)+1;        Select(maxfdp, &rset, NULL, NULL, NULL);        if(FD_ISSET(fileno(fp), &rset)){            if(Fgets(fp, sendbuf, MAXLINE) != EOF)                return;            Write(sockfd, sendbuf, strlen(sendbuf));        }        if(FD_ISSET(sockfd, &rset)){            int n;            if((n =  Readline(sockfd, recvbuf, MAXLINE)) == 0)                std::cout << "do_client: server terminaled\n";            Fputs(recvbuf, stdout);        }    }}


在cilent的发送速度和server相同的情况下,当在client通过循环发送8个请求给server后,当打算发送第九个请求的时候Fgets函数读取到了EOF,

这个时候返回空指针,这个时候函数返回。但是client的接受端并没有完全接受到来之server处理完的信息函数就返回了,这样就会造成数据的丢失。

因为虽然我们完成了从套接字的读入,但是请求可能还在前往去服务器的路上,或者是请求对应的处理消息还在从服务器到客服端的路上。




针对上诉的描述问题我们给出了如下的解决方案。

#include "unp"#define MAXLINE 4096void doClient_echo(FILE *fp, int sockfd){    int maxfdp;    fd_set rset;    FD_ZERO(&rset);    int stdineof = 0;    char sendbuf[MAXLINE],recvbuf[MAXLINE];    for(;;){        if(stdineof == 0)            FD_SET(fileno(fp), &rset);        FD_SET(sockfd, &rset);        maxfdp = fileno(fp) + 1;        Select(maxfdp, &rset, NULL, NULL, NULL);        if(FD_ISSET(sockfd, &rset)){            int n;            if((n = Readline(sockfd, recvbuf, MAXLINE)) == 0){                if(stdineof == 1)                    return;                else{                    std::cout << "Server terminated prematurely\n";                }            }            Write(STDOUT_FILENO, recvbuf, n);        }        if(stdineof == 0 && FD_ISSET(fileno(fp), &rset)){            int n;            if((n = Readline(fileno(fp), sendbuf, MAXLINE)) == EOF){                stdineof = 1;                Shutdown(sockfd, SHUT_WR);                FD_CLR(fileno(fp), &rset);                continue;            }            Write(sockfd, sendbuf, n);        }    }}


根据要求只有当请求发送完毕后,然后client接收了处理的全部要求,然后才能返回函数。但是根据TCP|IP四次握手的原则,所有的请求发送完毕后,通过调用shutdown函数半关闭套接字写入端口,最后一个信息就是有cilent发送一个FIN单字节,促使server被动关闭套接字,进入tcp|ip四次挥手过程。(该过程不在详述,请参考待定)


1.stdineof = 0,表示fp文件指针还可以继续读取,请求。当fp文件读取完成后,Readline函数返回EOF表示请求读取完毕,这个时候通过调用shutdown函数半关闭套接字写入端口,然后将该文件fp对应的文件描述符号,从rset描述符集合中除去。

2.目前等待一直从套接字描述符号中读取对应的请求的处理,并且答应出来,当读写出来的数据为“处理n”的时候表示是对“请求n”的处理结果,在sockfd读取结果为0的时候,表示收到的连接为ACK,如果stdineof = 1,那么该ACK很有可能就是由自己发送的FIN结束请求的应答,以此来表示结束函数。

原创粉丝点击