2

epoll を使用してイベント ベースのサーバーを作成したいと考えています。

各クライアントには個別の要求があり、サーバーはそれらに応答する必要があります。サーバーは接続を待機し、接続が利用可能になると、読み取りのためにキューに入れられます。データはクライアントから読み取られ、書き込みのためにキューに入れられます。データを処理した後、それぞれに適切な応答を送信する必要があります。

すべての操作は非同期になります。

問題は、ソケットが書き込みの準備ができているときに、どの応答がどのソケットに対するものであるかをどのように判断できるかということです?? 1 つの方法として、(ソケット、データ) タプルを格納することはできますが、それは一種の悪いプログラミングです。

各ソケットまたは各 epoll イベントにコンテキストを割り当てて、どのデータがどのソケットに属しているかを判断できるかどうか疑問に思います。

何か案が?

epoll の代わりに SIGIO を使用することに関する提案はありますか? コンテキストをファイル記述子またはシグナルに割り当てることができれば (私は Linux プログラミングに精通していません)、無期限にスリープしてシグナルを待つことができます...

ネットワークのことは忘れて、この例を見てください。事前に作成した FIFO を開き、SIGIO を取得するまでスレッドを一時停止します。別のケースでは、10 個の FIFO を開き、必要なときにそれぞれに乱数を割り当てたとします。その番号をコンソールに出力します。どういうわけか、番号を取得できる必要があります。おそらく、ファイル記述子にコンテキストを割り当てることができますか?

#include <stdlib.h>
#include <stdio.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
static void sigioHandler(int sig)
{
}

int main()
{
    int fd, epfd, ret, i, nr_events, flags;
    struct sigaction sa;
    struct epoll_event event, *events;
    char buf[10];
    memset(buf, 0, 10);
    sa.sa_flags = SA_RESTART;
    sa.sa_handler = sigioHandler;
    if (sigaction(SIGIO, &sa, NULL) == -1)
    {
        perror("sigaction");
        exit(1);
    }
    events = malloc (sizeof (struct epoll_event) * 10);
    if (!events) {
          perror ("malloc");
          return 1;
  }

    fd = open("/tmp/foo", O_RDONLY);

    if(fcntl(fd, F_SETOWN, getpid())==-1){
        perror("own");
        exit(1);
    }
    flags = fcntl(fd, F_GETFL);
    if(fcntl(fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK)==-1){
        perror("set");
        exit(1);
    }
    read(fd, buf, 10);
    epfd = epoll_create(10);
    if(epfd<0)
        perror("epoll_create");

    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET;

    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
    if(ret)
        perror("epol_ctl");
    while(1){
        pause();
        nr_events = epoll_wait (epfd, events, 10, -1);
        if (nr_events < 0) {
            perror ("epoll_wait");
            free (events);
            return 1;
        }

        for (i = 0; i < nr_events; i++) {
            if(events[i].events & EPOLLIN)
            {
                read(events[i].data.fd, buf, 10);
                if(buf[0] == '#')
                    goto end;
                printf("%s", buf);
            }
        }
    }
end:
    free (events);

    close(epfd);
    close(fd);
    return 0;
}

少し変更しました:

static void sigioHandler(int status, siginfo_t *ioinfo, void * context)
{
    if(ioinfo == NULL)
        return;

    switch (ioinfo->si_code)
    {
        case POLL_IN:
            printf("signal received for input chars.sig:%d -%d\n",status, ioinfo->si_code);
            break;

        case POLL_OUT:
        default:
            printf("signal received for something else.sig:%d -%d\n",status, ioinfo->si_code);
            break;
    }
}

in main:
...
sa.sa_sigaction = sigioHandler;
...

奇妙なセグメンテーション違反が発生します。

FreeBSD の「mac_set_fd(int fd, mac_t label);」この問題に関連しています。

4

3 に答える 3

2

Benito が指摘したように、epoll_ctl に渡される epoll_event 構造体には data_ptr フィールドがあります。完全に正しいために、コンテキストを作成するフィールドは次のように定義されます

typedef union epoll_data 
    void        *ptr;
    int          fd;
    uint32_t     u32;
    uint64_t     u64;
} epoll_data_t;

すなわち組合です。fd フィールドは、多くの例でコンテキストに使用されることがよくあります。しかし、ptr フィールドは、ソケットまたは接続のすべての状態を含むオブジェクトを指すために使用できる C++ で最も有用であることがわかりました。

次のようなオブジェクトがあるとします。

class Connection
{
  private:
    int m_fd;

  public:
    Connection(int fd) : m_fd(fd) {}

    // Other methods like Send/Receive
};


/////
// Somewhere in your server accept loop for a server
struct sockaddr sin;
socklen_t len = sizeof(sin);
int fd = accept(s_listen, &sin, &len);
if (fd == -1) {
    // handle error
}

// or if it's a client you'd have just created the socket with a call to socket()

// and then you add the new connection to epoll.
epoll_event ev;
ev.data.ptr = new Connection(fd);
ev.events = EPOLLIN | EPOLLOUT | EPOLLRDHUP;    
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
    ; // handle error
}

後で epoll_wait を使用して epoll イベントを取得すると、event.data.ptr にアクセスして Connection タイプにキャストし、デフォルト メソッドを呼び出してイベントを処理できます。その後、クラスは適切なメソッドを呼び出して、読み取り/書き込みなどの作業を完了することができます。

于 2014-06-12T03:55:59.783 に答える
2

epoll_ctl() に渡し、epoll_wait() によって埋められるepoll_event構造体には、 data_ptrフィールドがあります。

于 2013-11-04T08:15:44.757 に答える