Linux非ブロックIO(8)epollを用いて非ブロックのリターンサーバを再実現

9097 ワード

本文にはあまり内容がありません.主にいくつか前に述べた注意点です.
1つはepollのfdを再充填する必要があることです.tcp_connection_tのポインタは配列に保存されているので,この配列に基づいてfdのリスニングイベントを再充填する.
//    epoll fd     
        int i;
        for(i = 0; i < EVENTS_SIZE; ++i)
        {
            if(connsets[i] != NULL)
            {
                int fd = i; //fd
                tcp_connection_t *pt = connsets[i]; //tcp conn
                uint32_t event = 0;
                if(buffer_is_readable(&pt->buffer_))
                    event |= kWriteEvent;
                if(buffer_is_writeable(&pt->buffer_))
                    event |= kReadEvent;
                //      
                epoll_mod_fd(epollfd, fd, event);
            }
        }

2つ目は、接続を確立する際に必要な作業です.
1.新規tcp_connection_t構造、初期化
2.fdをepollに追加し、イベントを傍受しない
3.tcp_をconnection_tのポインタは配列に加わる.
コードは次のとおりです.
//    
int peerfd = accept4(listenfd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
if(peerfd == -1)
      ERR_EXIT("accept4");
//  tcp  
tcp_connection_t *pt = (tcp_connection_t*)malloc(sizeof(tcp_connection_t));
buffer_init(&pt->buffer_);
//  tcp    connsets
connsets[peerfd] = pt;
epoll_add_fd(epollfd, peerfd, 0);

接続を閉じるには、次の必要があります.
//close
epoll_del_fd(epollfd, fd);
close(fd);
free(pt);
connsets[fd] = NULL;

 
もう一つ:fdとconnsetsの関係を前に記録し、配列の下付きを採用していますが、実際にはepollのdataにポインタを格納することもできます.
typedef union epoll_data {
   void        *ptr;
   int          fd;
   uint32_t     u32;
   uint64_t     u64;
} epoll_data_t;

struct epoll_event {
   uint32_t     events;      /* Epoll events */
   epoll_data_t data;        /* User data variable */
};

我々はdataという連合体に対してfdではなくptrを用いてtcp_を指すconnection_tのポインタ.しかし、fdをtcp_に格納する必要があります.connection_tデータ構造中.
ここでは便宜上、従来の方法を採用しており、読者は自分で試してみることができる.
 
完全なコードは次のとおりです.
#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <sys/socket.h>
#include "sysutil.h"
#include "buffer.h"
#include <assert.h>
#include <sys/epoll.h>

#define EVENTS_SIZE 1024

typedef struct{
    buffer_t buffer_;
} tcp_connection_t; //    TCP  

tcp_connection_t *connsets[EVENTS_SIZE]; //   fd TCP     

int main(int argc, char const *argv[])
{
    //    fd
    int listenfd = tcp_server("localhost", 9981);
    //   fd      
    activate_nonblock(listenfd);

    //   connsets
    int ix;
    for(ix = 0; ix < EVENTS_SIZE; ++ix)
    {
        connsets[ix] = NULL;
    }


    //   epoll
    int epollfd = epoll_create1(0);
    epoll_add_fd(epollfd, listenfd, kReadEvent);
    struct epoll_event events[1024];


    while(1)
    {
        //    epoll fd     
        int i;
        for(i = 0; i < EVENTS_SIZE; ++i)
        {
            if(connsets[i] != NULL)
            {
                int fd = i; //fd
                tcp_connection_t *pt = connsets[i]; //tcp conn
                uint32_t event = 0;
                if(buffer_is_readable(&pt->buffer_))
                    event |= kWriteEvent;
                if(buffer_is_writeable(&pt->buffer_))
                    event |= kReadEvent;
                //      
                epoll_mod_fd(epollfd, fd, event);
            }
        }

        //epoll  fd
        int nready = epoll_wait(epollfd, events, 1024, 5000);
        if(nready == -1)
            ERR_EXIT("epoll wait");
        else if(nready == 0)
        {
            printf("epoll timeout.
"); continue; } // fd for(i = 0; i < nready; ++i) { int fd = events[i].data.fd; uint32_t revents = events[i].events; if(fd == listenfd) // listen fd { if(revents & kReadREvent) { // int peerfd = accept4(listenfd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC); if(peerfd == -1) ERR_EXIT("accept4"); // tcp tcp_connection_t *pt = (tcp_connection_t*)malloc(sizeof(tcp_connection_t)); buffer_init(&pt->buffer_); // tcp connsets connsets[peerfd] = pt; epoll_add_fd(epollfd, peerfd, 0); } } else // fd { // tcp_connection_t *pt = connsets[fd]; assert(pt != NULL); if(revents & kReadREvent) { if(buffer_read(&pt->buffer_, fd) == 0) { //close epoll_del_fd(epollfd, fd); close(fd); free(pt); connsets[fd] = NULL; continue; // } } if(revents & kWriteREvent) { buffer_write(&pt->buffer_, fd); } } } } close(listenfd); return 0; }

 
以下、epollのETモードを使用する.