我可以使用select来组合stdin并接受吗?

时间:2012-07-28 21:34:38

标签: c++ select pthreads

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

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

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

由于某种原因,我无法再从stdin读取输入。当我从我的fd集中删除两个fds中的任何一个时,这工作正常,另一个ome按预期执行。但是当我把它们都放进去时,虽然acceptfd仍然接受传入连接,但是stdinfd无法响应终端输入。

有谁知道我在这里做错了什么?这种方法本质上是有缺陷的吗?我应该专注于将这两个动作保持为单独的线程并找出一种干净利落的方法吗?

感谢您阅读!!

2 个答案:

答案 0 :(得分:2)

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

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

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

您可以使用gdb之类的调试器进行调试,或者只使用strace

答案 1 :(得分: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;

}