linux平台下gSOAP使用了传统的select模型,windows下gSOAP使用了poll模型,本文主要介绍linux平台下的select模型。
1.select函数
int select(int maxfdp1,fd_set*readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)
返回值:就绪描述符的数目,超时返回0,出错返回-1
2.函数参数介绍如下:
(1)第一个参数maxfdp1指定待测试的描述字个数,它的值是待测试的最大描述字加1(因此把该参数命名为maxfdp1),描述字0、1、2...maxfdp1-1均将被测试。
因为文件描述符是从0开始的。
(2)中间的三个参数readset、writeset和exceptset指定我们要让内核测试读、写和异常条件的描述字。如果对某一个的条件不感兴趣,就可以把它设为空指针。struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符,可通过以下四个宏进行设置:
void FD_ZERO(fd_set*fdset); //清空集合
void FD_SET(int fd, fd_set *fdset); //将一个给定的文件描述符加入集合之中
void FD_CLR(int fd, fd_set *fdset); //将一个给定的文件描述符从集合中删除
int FD_ISSET(int fd, fd_set *fdset); // 检查集合中指定的文件描述符是否可以读写
(3)timeout告知内核等待所指定描述字中的任何一个就绪可花多少时间。其timeval结构用于指定这段时间的秒数和微秒数。
struct timeval{
long tv_sec; //seconds
long tv_usec; //microseconds
};
这个参数有三种可能:
(1)永远等待下去:仅在有一个描述字准备好I/O时才返回。为此,把该参数设置为空指针NULL。
(2)等待一段固定时间:在有一个描述字准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微秒数。
(3)根本不等待:检查描述字后立即返回,这称为轮询。为此,该参数必须指向一个timeval结构,而且其中的定时器值必须为0。
3.select的几大缺点:
1〉每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
2〉同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
3〉select支持的文件描述符数量太小了,默认是1024
4. soap_accept的工作流程:
1.主函数for循环,调用soap_accept函数,返回得到client的socket
2.soap_accept内部调用tcp_select
tcp_select(struct soap *soap, SOAP_SOCKETsk, int flags, int timeout){
r =select((int)sk + 1, rfd, sfd, efd, &tv);//当server接受到client的连接,该函数返回
if(r > 0)
{ r= 0;
if ((flags & SOAP_TCP_SELECT_RCV) && FD_ISSET(sk, rfd))//如果是读,将r置位为读,返回
r |= SOAP_TCP_SELECT_RCV;
if ((flags & SOAP_TCP_SELECT_SND) && FD_ISSET(sk, sfd))
r |= SOAP_TCP_SELECT_SND;
if ((flags & SOAP_TCP_SELECT_ERR) && FD_ISSET(sk, efd))
r |= SOAP_TCP_SELECT_ERR;
}
}
3.soap_accept内部接着调用soap->faccept,然后返回soap->socket
soap->socket = soap->faccept(soap, soap->master, (structsockaddr*)&soap->peer, &n);
该函数为函数指针,函数原型为
tcp_accept(struct soap*, SOAP_SOCKET,struct sockaddr*, int*) {
fd = accept(s, a, (SOAP_SOCKLEN_T*)n);
}