简体   繁体   English

Linux C ++套接字选择循环

[英]linux C++ socket select loop

I'm having a little trouble with sockets, when looping I'm not receiving data except for the first loop, it's timing out each time. 我在使用套接字时遇到了一些麻烦,在循环时,除了第一个循环外,我没有收到任何数据,每次都超时。 If I close and reopen the socket each loop though I seem to be getting the data correctly. 如果我关闭并重新打开套接字,则每个循环虽然我似乎都能正确获取数据。 Any ideas as to why? 有什么想法吗?

Example of looping without closing: 不关闭而循环的示例:

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;
        }
    }
}

Example of looping with closing: 关闭循环示例:

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);
}

The select() function uses the specified file descriptor mask to determine which file descriptors to monitor for an event (read, write, etc.). select()函数使用指定的文件描述符掩码来确定要监视事件(读取,写入等)的文件描述符。 When a file descriptor is available for an I/O activity (read, write) the select() function modifies the descriptors to indicate which of the files are ready for the given I/O action. 当文件描述符可用于I / O活动(读,写)时, select()函数将修改描述符,以指示哪些文件已为给定的I / O操作准备就绪。

See this article on the select function and the macros/functions used with the file descriptors . 有关选择函数和与文件描述符一起使用的宏/函数的信息,请参见本文。

Old style Unix type programs often treated the file descriptor as a bit mask and just checked the bits. 旧式的Unix类型程序通常将文件描述符视为位掩码,而只是检查位。 However the actual implementation of the file descriptor can vary by compiler so it is best to use the standard file descriptor macros/functions to set, clear, and test the various file descriptors. 但是,文件描述符的实际实现会因编译器而异,因此最好使用标准文件描述符宏/函数来设置,清除和测试各种文件描述符。

So when using the select() function you need to use FD_ZERO() and FD_SET() so that you will set the specific file descriptors that you want for this particular call to the select() function. 因此,在使用select()函数时,您需要使用FD_ZERO()FD_SET()以便您可以为此select()函数设置特定的文件描述符。 When select() returns, it will indicate which of the file descriptors designated are actually ready to be used for the I/O action (read, write, etc.). select()返回时,它将指示指定的哪些文件描述符实际上已准备好用于I / O操作(读取,写入等)。

So your code will actually be something like: 因此,您的代码实际上将类似于:

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;
        }
    }

However what you really should do is to use the FD_ISSET() function to check which particular file descriptors are ready for use. 但是,您真正应该做的是使用FD_ISSET()函数来检查准备使用哪些特定的文件描述符。 In your case you have only the one but in a situation where there were multiple descriptors you would want to use the FD_ISSET() function as well. 在您的情况下,您只有一个,但是在有多个描述符的情况下,您也想使用FD_ISSET()函数。

When select returns anything except -1 it is considered success and the fd sets are modified to say which ones are ready or have an error. select返回除-1以外的任何值时,被认为是成功的,并且修改了fd集以表明哪些已就绪或有错误。

From the POSIX spec : 根据POSIX规范

Upon successful completion, the pselect () or select () function shall modify the objects pointed to by the readfds , writefds , and errorfds arguments to indicate which file descriptors are ready for reading, ready for writing, or have an error condition pending, respectively, and shall return the total number of ready descriptors in all the output sets. 成功完成后, pselect ()或select ()函数应修改readfdswritefdserrorfds参数所指向的对象,以分别指示哪些文件描述符已准备好读取,准备好写入或有错误状态待处理。 ,并应在所有输出集中返回就绪描述符的总数。

If a timeout occurs, then it returns zero meaning none of the descriptors became ready, and no bits will be set in the fd sets. 如果发生超时,则它将返回零,这意味着没有一个描述符准备就绪,并且不会在fd集中设置任何位。

So when your call times out there are no bits set in rfds , and so on the next loop when you call select you are asking it to wait for an empty set, which will never return a positive value because if you wait for zero FDs then you will never get a non-zero number of ready FDs! 因此,当你调用超时没有在设置的位rfds当你打电话,等下一个循环select你问它等待空集,因为如果你等待零个文件描述符然后将永远不会返回正值您将永远不会获得非零数量的可用FD!

You need to remember that rfds is both an input parameter and an output parameter, and so ensure it is set correctly before each call to select . 您需要记住, rfds既是输入参数又是输出参数,因此请确保在每次调用select之前正确设置了rfds

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM