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

一.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 4096
void 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结束请求的应答,以此来表示结束函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值