关于select()的讨论

时间:2017-04-24 14:52:28

标签: select network-programming

有一些我无法理解的选择(),我希望你的导游。当我读到这个功能时,我发现了

  

select()函数为您提供了一种同时检查的方法   多个套接字,看看他们是否有数据等待recv()d,或者是否   你可以发送()数据给他们没有阻止,或者如果有一些例外   发生。

1)我理解的第一件事是这个函数可以并行检查套接字。现在假设sock1和sock2完全同时接收数据包(来自sock1的packet1和来自sock2的packet2)并且每个数据包都需要完成一些进程。是并行处理数据包?或者packet1将处理然后数据包2将处理? (例如,在以下代码中)

int rv = select(maxSd, &readfds, NULL, NULL, NULL);

if (rv == -1) {
    perror("select"); // error occurred in select()
} else if (rv == 0) {
    printf("Timeout occurred!  No data after 10.5 seconds.\n");
} else {
    // one or both of the descriptors have data
    if (FD_ISSET(sock1, &readfds)) {
        printf("socket %i RECEIVED A PACKET \n", sock1);
        recvlen = recvfrom(sock1, buf, BUFSIZE, 0, (struct sockaddr *)&remaddr1, &addrlen1);
        if (recvlen > 0) {
            buf[recvlen] = 0;
            printf("received message: \"%s\" (%d bytes)\n", buf, recvlen); 
            Packet mp;
            mp.de_packet((unsigned char *)buf,recvlen);
        }
        else {
            printf("uh oh - something went wrong!\n");

        }
    }
    if (FD_ISSET(sock2, &readfds)) {
        printf("socket %i RECEIVED A PACKET \n", sock2);
        recvlen2 = recvfrom(sock2, buf2, BUFSIZE, 0, (struct sockaddr *)&remaddr2, &addrlen2);
        if (recvlen2 > 0) {
            buf[recvlen2] = 0;
            printf("received message2: \"%s\" (%d bytes)\n", buf2, recvlen2);
            Packet mp;
            mp.de_packet((unsigned char *)buf,recvlen);
        }
        else 
            printf("uh oh - something went wrong2!\n");                        
    }
}

2)我对选择的另一个疑问与阻塞和非阻塞有关。 阻塞究竟是什么意思?是否意味着程序在此行停止直到事件发生? 我认为为了避免阻塞,可以使用timeval tv或fcntl()。还有更好的方法吗?

提前致谢

1 个答案:

答案 0 :(得分:1)

返回select后,如果它没有返回0或-1,你的程序需要循环 readfds 的所有元素并评估是否ISSET,它是否设置了相应的socket必须处理。所以,假设只有sock1和sock2在 readfds 中设置,你的代码也是正确的。 readfds 中套接字的评估通常由同一个线程顺序完成。然后,可以顺序地或并行地处理每个套接字中的分组。 必须清楚的是,两个插座完全相互独立,不存在竞争条件。所有这些都取决于你如何编程。例如,对于ISSET返回true的每个套接字,您可以生成一个处理它的线程,或者您可以将它传递给工作队列,以便一组工作线程并行处理每个工作线程。没有任何限制。你甚至可以并行检查readfs,例如你可以让一个线程检查集合的下半部分,另一个线程检查上半部分。这只是一个例子。同样,没有限制,只要您在应用程序中不产生任何竞争条件就可以很好地编程。

关于阻塞或非阻塞的概念,select将始终阻塞,直到集合中的套接字具有要处理的事件(读取,写入,异常)或存在超时(如果设置超时值)。

你也可以谈论阻塞和非阻塞套接字,这是不同的。阻塞套接字是可以在读取或写入操作中阻止的套接字。阻塞套接字将在读取操作中阻塞,直到准备好读取字节为止,如果发送缓冲区已满并且无法在缓冲区中写入字节,则它将在写入操作中阻塞(这可能发生在STREAM套接字中)。它将阻塞,直到它可以写入其字节。如果没有要读取的内容,非阻塞套接字将不会在读取操作中阻塞,函数read将返回-1并且 errno 将设置为EAGAIN或EWOULDBLOCK(请参阅:http://man7.org/linux/man-pages/man2/read.2.html

select 通常与非阻塞套接字一起使用,这样线程就会阻塞,直到有一个套接字可以处理。这很好,因为否则您的应用程序需要始终轮询非阻塞套接字,这是无效的。

选择会并行处理所有套接字,但只是检查是否有任何事件。 选择不会处理任何数据包,如果你注意你的例子,在选择返回后你的应用程序将从套接字读取数据,这可以顺序或并行完成。

我希望这个解释可以帮助你理解这个概念。