首页 > 解决方案 > C++ TCP 服务器:FD_ISSET() 并不总是在基本服务器中工作

问题描述

这是我即将实现的 HTTP 服务器的实现。

void    Webserv::resetFdSets()
{

    int         fildes;

    FD_ZERO(&to_read);
    FD_ZERO(&to_write);
    max_fd = -1;
    Server_map::iterator it;
    for (it = servers.begin(); it != servers.end(); ++it) //set server sockets to be read
    {
        fildes = it->second.getFd();
        FD_SET(fildes, &to_read);
        if (fildes > max_fd)
            max_fd = fildes;
    }
    std::list<int>::iterator iter;
    for (iter = accepted.begin(); iter != accepted.end(); ++iter) // set client sockets if any
    {
        fildes = (*iter);
        FD_SET(fildes, &to_read);
        FD_SET(fildes, &to_write);
        if (fildes > max_fd)
            max_fd = fildes;
    }
}

接受()

void    Webserv::acceptConnections()
{

   int         client_fd;
   sockaddr_in cli_addr;
   socklen_t   cli_len = sizeof(sockaddr_in);

   Server_map::iterator    it;
   for (it = servers.begin(); it != servers.end(); ++it)
   {
       ft_bzero(&cli_addr, sizeof(cli_addr));
       if (FD_ISSET(it->second.getFd(), &to_read)) // if theres data in server socket
       {
           client_fd = accept(it->second.getFd(), reinterpret_cast<sockaddr*>(&cli_addr), &cli_len);
           if (client_fd > 0) // accept and add client
           {
               fcntl(client_fd, F_SETFL, O_NONBLOCK);
               accepted.push_back(client_fd);
           }
           else
               throw (std::runtime_error("accept() failed"));
       }
    }
}

接收()

void    Webserv::checkReadSockets()
{

    char        buf[4096];
    std::string raw_request;
    ssize_t     bytes;
    static int  connections;

    std::list<int>::iterator    it;
    for (it = accepted.begin(); it != accepted.end(); ++it)
    {
        if (FD_ISSET(*it, &to_read))
        {
            ++connections;
            std::cout << "Connection counter : " << connections << std::endl;
            while ((bytes = recv(*it, buf, sizeof(buf) - 1, MSG_DONTWAIT)) > 0)
            {
                buf[bytes] = '\0';
                raw_request += buf;
            }
            std::cout << raw_request << std::endl;
        }
    }
}

发送()

void    Webserv::checkWriteSockets()
{

   char            buf[8096];
   char            http_success[] = {
       "HTTP/1.1 200 OK\r\n"
       "Server: This op server\r\n"
       "Content-Length: 580\r\n"
       "Content-Type: text/html\r\n"
       "Connection: Closed\r\n\r\n" 
   };

   std::list<int>::iterator it = accepted.begin();
   while (it != accepted.end())
   {
       int cliSock = (*it);
       if (FD_ISSET(cliSock, &to_write))
       {
           int     response_fd = open("hello.html", O_RDONLY);

           int bytes = read(response_fd, buf, sizeof(buf));
           send(cliSock, http_success, sizeof(http_success) - 1, MSG_DONTWAIT); // header
           send(cliSock, buf, bytes, MSG_DONTWAIT); // hello world

           close(cliSock);
           close(response_fd);
        
           it = accepted.erase(it);
       }
       else
           ++it;
   }
}

主循环:

void    Webserv::operate()
{

   int     rc;

   while (true)
   {
       resetFdSets(); // add server and client fd for select()
       if ((rc = select(max_fd + 1, &to_read, &to_write, NULL, NULL)) > 0) // if any of the ports are active
       {
           acceptConnections(); // create new client sockets with accept()
           checkReadSockets(); // parse and route client requests recv() ...
           checkWriteSockets(); // send() response and delete client
       }
       else if (rc < 0)
       {
           std::cerr << "select() failed" << std::endl;
           exit(1);
       }
   }
}

在 pastebin 上突出显示相同的代码 + Webserv 类定义:https ://pastebin.com/9B8uYumF

目前算法是这样的:

在检查所有客户端描述符时,FD_ISSET() 在 80% 的情况下不应该返回 false。因此,我无法始终如一地获得客户请求。奇怪的是,它更频繁地使用 fd_set 返回 true 来写入套接字。这是一个演示,其中 10 次成功读取中有 2 次使用上述代码:https ://imgur.com/a/nzc21LV

我知道新客户端总是被接受,因为它总是在 acceptConnections() 中输入 if 条件。换句话说,listen FD 被正确初始化。

编辑:这是它的初始化方式:

void    Server::init()
{
    if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        throw (std::runtime_error("socket() failed"));
    }
    fcntl(listen_fd, F_SETFL, O_NONBLOCK);

    int yes = 1;
    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
    if (bind(listen_fd, reinterpret_cast<sockaddr*>(&server_addr), sizeof(server_addr)))
    {
        throw (std::runtime_error("bind() failed"));
    }

    if (listen(listen_fd, 0))
        throw (std::runtime_error("listen() failed"));
}

标签: c++socketstcp

解决方案


推荐阅读