linux C ++套接字选择循环

时间:2014-09-17 11:06:12

标签: c++ c linux sockets select

我对套接字有点麻烦,在循环时我没有收到除第一个循环之外的数据,它每次都超时。如果我关闭并重新打开套接字每个循环虽然我似乎正确获取数据。关于为什么的任何想法?

不关闭循环的示例:

int socketHandle = socket(AF_INET,SOCK_DGRAM,0);

sockaddr_in serverAddr;

serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = inet_addr(/*UDP IP ADDRESS*/);
serverAddr.sin_port = htons(/*UDP PORT*/);

struct timeval tv;
fd_set rfds;

FD_ZERO(&rfds);
FD_SET(socketHandle, &rfds);

tv.tv_usec = 0.0;
int recVal = 0;
int sockLen = sizeof(serverAddr);
bind(socketHandle, (struct sockaddr*)&serverAddr, (socklen_t)sockLen);

bool timePassed = false;
time_t startListenTime = time(NULL);

tv.tv_sec = maxUpdateTime;

while(true)
{
    recVal = select(socketHandle + 1, &rfds, NULL, NULL, &tv);
    switch(recVal)
    {
        case(0):
        {
            //Timeout
            break;
        }
        case(-1):
        {
            //Error
             break;
        }
        default:
        {
            /*Packet Data Type*/ pkt;
            if(recvfrom(socketHandle, &pkt, sizeof(/*Packet Data Type*/), 0, (sockaddr*)&serverAddr, (socklen_t*)&sockLen) < 0)
            {
                //Failed to Recieve Data
                break;
            }
            else
            {
                //Recieved Data!!
            }
            break;
        }
    }
}

关闭循环的示例:

while(true)
{
    int socketHandle = socket(AF_INET,SOCK_DGRAM,0);

    sockaddr_in serverAddr;

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = inet_addr(/*UDP IP ADDRESS*/);
    serverAddr.sin_port = htons(/*UDP PORT*/);

    struct timeval tv;
    fd_set rfds;

    FD_ZERO(&rfds);
    FD_SET(socketHandle, &rfds);

    tv.tv_usec = 0.0;
    int recVal = 0;
    int sockLen = sizeof(serverAddr);
    bind(socketHandle, (struct sockaddr*)&serverAddr, (socklen_t)sockLen);


    bool timePassed = false;
    time_t startListenTime = time(NULL);

    tv.tv_sec = maxUpdateTime;

    recVal = select(socketHandle + 1, &rfds, NULL, NULL, &tv);
    switch(recVal)
    {
        case(0):
        {
            //Timeout
            break;
        }
        case(-1):
        {
            //Error
             break;
        }
        default:
        {
            /*Packet Datastructure*/ pkt;
            if(recvfrom(socketHandle, &pkt, sizeof(/*Packet Datastructure*/), 0, (sockaddr*)&serverAddr, (socklen_t*)&sockLen) < 0)
            {
                //Failed to read packet
                break;
            }
            else
            {
                //Read Packet!!
            }
            break;
        }
    }
    close(socketHandle);
}

2 个答案:

答案 0 :(得分:3)

select()函数使用指定的文件描述符掩码来确定要监视事件(读,写等)的文件描述符。当文件描述符可用于I / O活动(读,写)时,select()函数会修改描述符以指示哪些文件已准备好进行给定的I / O操作。

请参阅select function and the macros/functions used with the file descriptors上的这篇文章。

旧式Unix类型程序经常将文件描述符视为位掩码并仅检查位。但是,文件描述符的实际实现可能因编译器而异,因此最好使用标准文件描述符宏/函数来设置,清除和测试各种文件描述符。

因此,在使用select()函数时,您需要使用FD_ZERO()FD_SET(),以便为{(1}}此特定调用设置所需的特定文件描述符功能。当select()返回时,它将指示指定的哪个文件描述符实际上已准备好用于I / O操作(读,写等)。

所以你的代码实际上就像是:

select()

但是,您真正应该做的是使用while(true) { fd_set rfds; FD_ZERO(&rfds); FD_SET(socketHandle, &rfds); recVal = select(socketHandle + 1, &rfds, NULL, NULL, &tv); switch(recVal) { case(0): { //Timeout break; } case(-1): { //Error break; } default: { /*Packet Data Type*/ pkt; if(recvfrom(socketHandle, &pkt, sizeof(/*Packet Data Type*/), 0, (sockaddr*)&serverAddr, (socklen_t*)&sockLen) < 0) { //Failed to Recieve Data break; } else { //Recieved Data!! } break; } } 函数来检查哪些特定文件描述符可以使用。在您的情况下,您只有一个,但在有多个描述符的情况下,您也希望使用FD_ISSET()函数。

答案 1 :(得分:1)

select返回除-1之外的任何内容时,它被视为成功,并且修改了fd集以说明哪些已准备好或出错。

来自POSIX spec

  

成功完成后, pselect ()或 select ()函数将修改 readfds 指向的对象, writefds errorfds 参数分别指示哪些文件描述符已准备好进行读取,准备写入或者有错误条件待定,并且应返回所有准备描述符的总数输出集。

如果发生超时,则返回零意味着没有描述符准备就绪,并且fd集中不会设置任何位。

因此,当你的通话超时时,rfds中没有设置位,当你调用select时,下一个循环就是这样,你要求它等待空集,这将永远不会返回一个正值,因为如果你等待零FD,那么你永远不会得到非零数量的就绪FD!

您需要记住rfds既是输入参数又是输出参数,因此请确保在每次调用select之前正确设置它。