基于事件驱动网络服务器实现

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;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值