2

ターミナルからユーザー入力を定期的に取得するサーバーを C++/Linux に実装しようとしています。最初に、この動作を処理するために 2 つの別個のスレッドを実装しました。しかし、ユーザーがサーバーをシャットダウンしたい場合に備えて、サーバー スレッドをキャンセルするには pthread_cancel のようなものが必要になることに気付きました。

次に、両方のアクションを同じスレッドで処理する方がよいと判断したため、リソースのリークを心配する必要はありません。したがって、私が今持っているのは、stdin fd と私の受け入れ fd を選択する「選択」呼び出しです。私のコードは次のようになります...

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

何らかの理由で、標準入力から入力を読み取ることができなくなりました。fd セットから 2 つの fd のいずれかを削除すると、これは正常に機能し、他の ome は期待どおりに動作します。しかし、両方をそのままにしておくと、acceptfd は引き続き着信接続を受け入れますが、stdinfd は端末入力に応答しません。

ここで私が間違っているかもしれないことを誰かが知っていますか? このアプローチは本質的に欠陥がありますか? 2 つのアクションを別々のスレッドとして維持し、代わりにきれいに終了する方法を見つけることに集中する必要がありますか?

読んでくれてありがとう!!

4

2 に答える 2

2

Ambroz がコメントしたように、標準入力とリッスンされた fd の多重化が可能です。

しかしselect、古い、ほとんど時代遅れの syscall ですpoll(2)。それでもselect(2)syscallを使用することに固執する場合は、ループ内readfdsで最初にクリアする必要があります。FD_ZEROまた、FD_SETマクロは while ループ内にある必要があります。これselectは、readfds.

pollsyscall は、プロセスが持つことができるファイル記述子の数に有線の制限を課すselectためselect(通常は 1024 ですが、カーネルは現在、65536 などのより多くの fds を処理できます) よりも好ましいです。言い換えれば、selectすべての fd が < 1024 である必要があります (これは今日では誤りです)。poll任意の fd の任意のセットを処理できます。への最初の引数は、多重化する fds の数をサイズとpollする配列 (必要に応じて可能) です。callocあなたの場合、それは2つ(stdinと2番目にリッスンされた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 に答える