基於事件驅動網絡服務器實現

socket 的事件類型有 讀事件(socket鏈接也屬於讀事件)、寫事件、socket 關閉事件

事件處理方式無非就是添加事件、刪除事件、分發執行事件

大致邏輯就是:


事件驅動的網絡實現邏輯

Loop:
    EventOp->dispatch()  // 由 select或epoll 進行事件監聽
    將監聽的事件添加到事件列表裏
    for(ev : event_list):
        if ev & ev_read && fd 是server 端
           建立與客戶端的鏈接,標識爲讀事件加入事件列表
        
        if ev & ev_read:
            read_msg();
        if ev & ev_write:
            send_msg()
        if ev & ev_closed:
            close_session()

也可以將上面的邏輯放入多線程中,就是大家經常講的基於事件高性能服務器的實現。還有一點需要注意,socket 一定是非阻塞的,否則循環會卡住。

可以參考libevent、redis的網絡實現,邏輯就是上面的形式。


事件後端基本上都是利用select、epoll、kequeue實現
 

class EventOp
{
public:
    virtual bool Init();
    bool Dispatch();
    virtual bool InitOp() = 0;
    virtual bool AddEvent(socket_t fd, int mask) = 0;  # mask 就是ev_read ev_write ev_closed
    virtual bool DelEvent(socket_t fd, int mask) = 0;
    virtual bool Dispatch(struct timeval* tv) = 0;
    virtual bool Clear() = 0;
};

class SeEpoll : public EventOp
{
public:
    SeEpoll();
    ~SeEpoll();
    virtual bool InitOp();
    virtual bool AddEvent(socket_t fd, int mask);
    virtual bool DelEvent(socket_t fd, int mask);
    virtual bool Dispatch(struct timeval* tv);
    virtual bool Clear();
};


bool SeEpoll::InitOp()
{
#ifdef EVENT_HAVE_EPOLL_CREATE1
    mEpollOp.epfd = epoll_create1(EPOLL_CLOEXEC);
#endif
    if (mEpollOp.epfd == -1)
    {
        mEpollOp.epfd = epoll_create(1024);
        Assert(mEpollOp.epfd != -1);
    }
    mEpollOp.events = new epoll_event[EPOLL_EVENT_NUM];
    if (mEpollOp.events == nullptr)
    {
        return false;
    }
    return true;
}

bool SeEpoll::AddEvent(socket_t fd, int mask)
{
    uint32_t events = 0;
    if (mask & EV_READ)
    {
        events = EPOLLET | EPOLLONESHOT | EPOLLIN;
    }
    if (mask & EV_WRITE)
    {
        events = EPOLLET | EPOLLONESHOT | EPOLLOUT;
    }
    struct epoll_event ev = { 0 };
    short op = 0;
    auto it = mEvents.find(fd);
    if (it == mEvents.end())
    {
        ev.data.fd = fd;
        ev.events = events; // EPOLLET | EPOLLONESHOT | EPOLLIN | EPOLLOUT
        mEvents.emplace(fd, ev);
        op = EPOLL_CTL_ADD;
    }
    else
    {
        ev = it->second;
        ev.events = events;
        op = EPOLL_CTL_MOD;
    }
    if (epoll_ctl(mEpollOp.epfd, op, fd, &ev) == 0)
    {
        return true;
    }
    switch (op) {
    case EPOLL_CTL_MOD:
        if (errno == ENOENT) {
            if (epoll_ctl(mEpollOp.epfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
                return false;
            }
            return true;
        }
        break;
    case EPOLL_CTL_ADD:
        if (errno == EEXIST) {
            if (epoll_ctl(mEpollOp.epfd, EPOLL_CTL_MOD, fd, &ev) == -1) {
                return false;
            }
            return true;
        }
        break;
    case EPOLL_CTL_DEL:
        if (errno == ENOENT || errno == EBADF || errno == EPERM) {
            return true;
        }
        break;
    default:
        break;
    }
    return false;
}

bool SeEpoll::DelEvent(socket_t fd, int mask)
{
    auto it = mEvents.find(fd);
    if (it == mEvents.end())
    {
        return false;
    }
    if (epoll_ctl(mEpollOp.epfd, EPOLL_CTL_DEL, fd, nullptr) == 0)
    {
        return true;
    }
    return false;
}

bool SeEpoll::Dispatch(struct timeval* tv)
{
    int ret, nEvents = 0;
    ret = epoll_wait(mEpollOp.epfd, mEpollOp.events, EPOLL_EVENT_NUM,
        tv ? (tv->tv_sec * 1000 + tv->tv_usec / 1000) : -1);
    if (ret == -1)
    {
        if (SOCKET_ERR_RW_RETRIABLE(errno))
        {
            return true;
        }
        fprintf(stderr, "epoll_wait error %d:%s", errno, strerror(errno));
        return false;
    }
    if (ret > 0)
    {
        nEvents = ret;
        for (int i = 0; i < nEvents; i++)
        {
            struct epoll_event ev = mEpollOp.events[i];
            int mask = 0;
            if (ev.events & (EPOLLHUP | EPOLLERR))
            {
                mask = EV_READ | EV_WRITE;
            }
            else
            {
                if (ev.events & EPOLLIN)
                {
                    mask |= EV_READ;
                    AddEvent(ev.data.fd, mask);
                }
                if (ev.events & EPOLLOUT)
                {
                    mask |= EV_WRITE;
                    AddEvent(ev.data.fd, mask);
                }
                if (ev.events & EPOLLRDHUP)
                {
                    mask |= EV_CLOSED;
                    DelEvent(ev.data.fd, EPOLLRDHUP);
                }
            }
            if (mask == 0)
            {
                continue;
            }
            SetEvent(ev.data.fd, mask);
        }
    }
    return true;
}

bool SeEpoll::Clear()
{
    SocketCloseOnExec(mEpollOp.epfd);
    mEpollOp.epfd = -1;
    delete[] mEpollOp.events;
    return true;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章