最近在做项目的时候,遇到一个问题:就是阻塞模式下的UDP在接收(recvfrom)的时候,如果没有收到数据包,程序会一直阻塞。
在Windows下可以通过设置超时时间来解决这个问题。
struct timeval TimeOut;
TimeOut.tv_sec = 1;
TimeOut.tv_usec = 0;
::setsockopt(sockServer, SOL_SOCKET, SO_RCVTIMEO, (char *)&TimeOut, sizeof(TimeOut));
但是在wince6.0上,上面的代码不起效果,然后上网查了一下,可以用select来解决UDP的阻塞问题,在wince6.0上也支持。
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, const struct timeval* timeout);
select函数可以监视需要监视的文件描述符的变化情况,读写或是异常。文件描述符,在windows上即文件句柄,socket就是一个文件描述符。
参数列表
int nfds: 是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以忽略,通常设置为0。
fd_set* readfds: 需要监视读变化的文件描述符的集合,比如需要监视一个socket的数据接收,就可以将这个socket加入到这个集合中。如果不关心任何文件描述符的读变化,可以传入NULL。
fd_set* writefds:需要监视写变化的文件描述符的集合,比如需要监视一个socket的数据发送,就可以将这个socket加入到这个集合中。如果不关心任何文件描述符的写变化,可以传入NULL。
fd_set* exceptfds:同上面两个参数类似,监视异常的文件描述符的集合。
const struct timeval* timeout:设置select的超时时间,
1)传入NULL值:将设置select为阻塞状态,一直等到监视的文件描述符集合中的某个文件描述符发生变化;
2)传入0秒0毫秒:select变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
3)timeout的值大于0:就是等待的超时时间,即 select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回;
返回值
< 0:select发生错误。
> 0:监视的文件描述符发生变化。
= 0:等待超时,监视的文件描述符没有变化。
UDP接收数据包的代码
bool RecvMsg(char* recvbuf)
{
fd_set fdRead;
timeval timeout;
timeout.tv_sec = 3
timeout.tv_usec = 0;
while(true)
{
FD_ZERO(&fdRead);
FD_SET(m_sock, &fdRead);
int ret = select(0, &fdRead, NULL, NULL, &timeout);
if(ret == SOCKET_ERROR)
{
return false;
}
else if(ret == 0)//超时
{
return false;
}
else if(ret > 0)
{
if(FD_ISSET(m_sock, &fdRead))
{
SOCKADDR_IN addrSrv;
int nLen = sizeof(addrSrv);
int nRecvBytes = recvfrom(m_sock, recvbuf, RECVBUFFER_SIZE, 0, (SOCKADDR*)&addrSrv, &nLen);
if(nRecvBytes == SOCKET_ERROR || nRecvBytes <= 0)
{
return false;
}
else
{
return true;
}
}
}
}
return false;
}