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