c - epoll 事件循环内阻塞操作的解决方案

标签 c sockets concurrency server epoll

我的 TCP 服务器中有一个 epoll 事件循环来处理客户端连接并从客户端读取数据。

while(1) {
    int n, i;

    n = epoll_wait(efd, events, 64, -1); // This is blocking. It waits till new events arrive

    for(i = 0; i < n; i++) {
        if((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) {
          /* An error has occured on this fd, or the socket is not
             ready for reading */
      dzlog_error("epoll error: %s", strerror(errno));
      close(events[i].data.fd);

      continue;
    } else if(sock == events[i].data.fd) { // Event on the server socket. Accept client connection
            while(1) {
                if((cli = accept(sock, (struct sockaddr *)&their_addr, &addr_size)) == -1) {
                    if((errno == EAGAIN) || (errno == EWOULDBLOCK)) { // We have processed all incoming connections
                        break;
                    } else {
                        dzlog_error("accept: %s", strerror(errno));
                        break;
                    }
                }

                dzlog_info("Client connected: Identifier - %d", cli);

                s = fcntl(cli, F_SETFL, O_NONBLOCK); // Make client socket non-blocking
               if(s == -1) {
                    dzlog_error("Client no block: %s", strerror(errno));
                    close(cli);
                    break;
                }

                event.data.fd = cli;
                event.events = EPOLLIN | EPOLLET;
                s = epoll_ctl (efd, EPOLL_CTL_ADD, cli, &event); // Add the client socket to the list of file descriptors to poll
                if(s == -1) {
                    dzlog_error("epoll_ctl: %s", strerror(errno));
                    close(cli);
                    break;
                }
            }

            continue;
        } else {
            readClientData(events[i].data.fd);
        }
    }
}

当有数据要从客户端套接字读取时,会调用readClientData函数。假设在该函数内,我们调用了一个数据库,该数据库从表中获取一些数据。如果由于某种原因,对数据库的调用挂起或花费的时间比预期长,其他等待连接或发送数据的客户端也会被阻塞。

例如,考虑以下场景:

  1. 客户端 1 连接到服务器
  2. 客户端 2 连接到服务器
  3. 客户端 1 向服务器发送数据(这将导致调用 readClientData 函数来处理数据)
  4. readClientData 函数调用数据库并等待响应。 (等待 10 秒,否则可能无限期挂起)
  5. 客户端 2 发送数据。无法处理此数据,因为服务器仍在等待客户端 1 的 readClientData 完成
  6. 新的客户端 3 尝试连接,但必须等待其连接被接受,因为服务器仍在处理来自客户端 1 的数据

有办法解决这个问题吗?

谢谢

最佳答案

您可以将单独的进程专门用于等待操作,例如数据库读取、监听套接字,以便您也可以使用事件循环来检查数据库进程的发送/接收完成情况

并在主进程中保留事件循环: 从客户端读取,以无等待模式写入数据库处理进程,返回事件循环检查数据库进程回复或来自客户端的新请求

关于c - epoll 事件循环内阻塞操作的解决方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45830135/

相关文章:

c - 如何让一个 pthread 向另一个 pthread 发出信号,表明它可以继续执行?

c - sysconf(_SC_CLK_TCK) 它返回什么?

c - 使用不同类型的字段填充结构

c - 为什么我们需要在 C 并发服务器中为每个客户端创建不同的进程?

java - 如何检查是否有任何线程在等待条件变量?

c - 为什么可变参数宏会给我一个错误?

c - 在 C 客户端-服务器程序中处理超时

java - 客户端-服务器多线程聊天应用程序。客户与客户的沟通

java - 显式锁定是否自动提供内存可见性?

programming-languages - 多核和并发-语言,库和开发技术