Linux C ++套接字选择循环

APo*_*ter 0 c c++ sockets linux 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

关闭循环示例:

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);
}
Run Code Online (Sandbox Code Playgroud)

Ric*_*ers 5

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

请参阅本文,了解select函数以及与文件描述符一起使用的宏/函数。

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

因此,在使用该select()函数时,需要使用FD_ZERO()和,FD_SET()以便您可以为此select()函数的特定调用设置所需的特定文件描述符。当select()返回时,则表示其指定的文件描述符,实际上准备用于I / O操作(读,写等)。

因此,您的代码实际上将类似于:

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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

但是,您真正应该做的是使用该FD_ISSET()功能来检查准备使用哪些特定的文件描述符。在您的情况下,您只有一个,但是在有多个描述符的情况下,您也想使用该FD_ISSET()函数。