为什么套接字在数千次迭代后才准备好读取?

why socket is ready to read only after thousands of iterations?

我正在编写我的 http 服务器。我使用 select 函数,看到套接字随时可以写入,但只能在数千次迭代后才能读取。如果我使用 select(Max+1, &rfd, NULL, NULL, NULL),我就没有这样的问题。为什么迭代了这么多次才准备好读取?

int iteration = -1;
while (true)
{
    iteration++;
    FD_ZERO(&rfd);
    FD_ZERO(&wfd);
    FD_SET(listeningSocket, &rfd);
    for (std::set<int>::iterator Iter = servingSockets.begin(); Iter != servingSockets.end(); Iter++)
    {
        FD_SET(*Iter, &rfd);
        FD_SET(*Iter, &wfd);
    }
    int Max = std::max(listeningSocket, *std::max_element(servingSockets.begin(), servingSockets.end()));
    res = select(Max + 1, &rfd, &wfd, NULL, NULL);
    if (res <= 0)
        continue;

    if (FD_ISSET(listeningSocket, &rfd))
    {
        if ((newSocket = accept(listeningSocket, (struct sockaddr *)&listeningSocketAddr, (socklen_t *)&listeningSocketAddr)) < 0)
            continue;
        fcntl(newSocket, F_SETFL, O_NONBLOCK);
        servingSockets.insert(newSocket);
    }

    for (std::set<int>::iterator Iter = servingSockets.begin(); Iter != servingSockets.end();)
    {
        if (FD_ISSET(*Iter, &rfd))
        {
            std::cout<<"iter in loop: "<<iteration<<std::endl;
            int bytes_recvd = recv(*Iter, request, request_buffer_size - 1, 0);
            if (bytes_recvd < 0)
            {
                fprintf(stderr, "error recv\n");
                shutdown(*Iter, SHUT_RDWR);
                close(*Iter);
                servingSockets.erase(*Iter);
                continue;
            }
            request[bytes_recvd] = '[=10=]';
            parse_http_request(request, &req);
        }
        if (FD_ISSET(*Iter, &wfd) && req.path[0] != '[=10=]')
        {
           
            send(*Iter, "HTTP/1.1 200 OK\n\n<h1><a href=\"\">external</a><br><a href=\"internal\">internal</a></h1>", 122, 0);
            shutdown(*Iter, SHUT_RDWR);
            close(*Iter);
            Iter = servingSockets.erase(Iter);
            continue;
        }
        Iter++;
    }
}

一个套接字将总是可写,除非你填满它的缓冲区。

因此 select 将在每次调用时立即 return,并将所有套接字标记为可写。

只有当您确实有东西要写入时,才将套接字添加到 write-set。一旦你写完所有你需要的,然后从集合中删除它们并且不要添加它们(直到下一次你需要写入套接字)。


当您不使用 write-set 时,select 将不会 return,直到 read-set 中有活动的套接字。