2

我正在尝试在 C++/Linux 中实现一个服务器,该服务器定期从终端获取用户输入。最初我实现了两个单独的线程来处理这种行为。但我意识到我需要类似 pthread_cancel 的东西来取消服务器线程,以防用户想要关闭服务器。

然后我决定在同一个线程中处理这两个动作可能会更好,所以我不必担心资源泄漏。所以我现在有一个“选择”调用,它选择标准输入 fd 以及我接受的 fd。我的代码看起来像这样......

fdset readfds;
FD_SET(acceptfd, &readfds);
FD_SET(stdinfd, &readfds);
while(1) {
  select(n, &readfds, NULL, NULL, NULL);
  ....
}

出于某种原因,我不再能够从标准输入读取输入。当我从我的 fd 集中删除两个 fd 中的一个时,这工作正常,另一个 ome 按预期执行。但是当我把它们都留在里面时,虽然 acceptfd 仍然接受传入的连接,但 stdinfd 无法响应终端输入。

有谁知道我在这里可能做错了什么?这种方法是否存在固有缺陷?我是否应该专注于将这两个动作保持为单独的线程并找出一种干净退出的方法?

谢谢阅读!!

4

2 回答 2

2

正如 Ambroz 评论的那样,多路复用标准输入和一些听过的 fd 是可能的。

但是select是一个旧的、几乎过时的系统调用,您应该更喜欢使用poll(2). 如果你坚持仍然使用select(2)系统调用,你应该首先清除循环内的readfds第一个。FD_ZERO并且FD_SET宏应该在 while 循环内,因为select允许修改readfds.

系统poll调用更可取,select因为select对进程可以拥有的文件描述符的数量施加了有线限制(通常为 1024,而现在内核能够处理更大数量的 fd,例如 65536)。换句话说,select要求每个 fd < 1024(今天是错误的)。poll能够处理任何一组任何 fd。第一个参数poll是一个数组(calloc如果你愿意,你可以),它的大小是你想要多路复用的 fd 的数量。在您的情况下,它是两个(stdin 和第二个侦听的 fd),因此您可以将其设为局部变量。确保在每次调用之前清除并初始化它poll

您可以使用调试器进行调试,例如gdb或仅使用strace

于 2012-07-28T21:39:39.510 回答
1

这段epoll代码对我有用:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 4711

int main(void) {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    addr.sin_addr.s_addr = htons(INADDR_ANY);
    bind(sockfd, (struct sockaddr*) &addr, sizeof (addr));
    listen(sockfd, 10);

    int epollfd = epoll_create1(0);
    struct epoll_event event;
    // add stdin
    event.events = EPOLLIN|EPOLLPRI|EPOLLERR;
    event.data.fd = STDIN_FILENO;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &event) != 0) {
        perror("epoll_ctr add stdin failed.");
        return 1;
    }
    // add socket
    event.events = EPOLLIN|EPOLLPRI|EPOLLERR;
    event.data.fd = sockfd;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &event) != 0) {
        perror("epoll_ctr add sockfd failed.");
        return 1;
    }

    char *line = NULL;
    size_t linelen = 0;
    for (;;) {
        int fds = epoll_wait(epollfd, &event, 1, -1);
        if (fds < 0) {
            perror("epoll_wait failed.");
            return 2;
        }
        if (fds == 0) {
            continue;
        }

        if (event.data.fd == STDIN_FILENO) {
            // read input line
            int read = getline(&line, &linelen, stdin);
            if (read < 0) {
                perror("could not getline");
                return 3;
            }
            printf("Read: %.*s", read, line);
        } else if (event.data.fd == sockfd) {
            // accept client
            struct sockaddr_in client_addr;
            socklen_t addrlen = sizeof (client_addr);
            int clientfd = accept(sockfd, (struct sockaddr*) &client_addr, &addrlen);
            if (clientfd == -1) {
                perror("could not accept");
                return 4;
            }
            send(clientfd, "Bye", 3, 0);
            close(clientfd);
        } else {
            // cannot happen™
            fprintf(stderr, "Bad fd: %d\n", event.data.fd);
            return 5;
        }
    }

    close(epollfd);
    close(sockfd);
    return 0;

}
于 2012-07-28T22:13:00.883 回答