Socket编程实践(11)

”,这个内存拷贝也省略了。

epoll的使用

epoll的接口非常简单,一共就3/4个函数:

int epoll_create(int size);int epoll_create1(int flags);int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

1.对于epoll_create1的flag参数:可以设置为0或EPOLL_CLOEXEC,为0时函数表现与epoll_create一致,EPOLL_CLOEXEC标志与open时的O_CLOEXEC标志类似,即进程被替换时会关闭打开的文件描述符(需要注意的是,epoll_create与epoll_create1当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/<pid>/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽)。

2.对于epoll_ctl,op参数表示动作,用三个宏来表示:

EPOLL_CTL_ADD

注册新的fd到epfd中

EPOLL_CTL_DEL

从epfd中删除一个fd

EPOLL_CTL_MOD

修改已经注册的fd的监听事件

3.对于epoll_wait:

events:结构体指针,一般是一个数组

maxevents:事件的最大个数,或者说是数组的大小

timeout:超时时间,含义与poll的timeout参数相同,设为-1表示永不超时;

4.epoll_event结构体

struct epoll_event{uint32_tevents;/* Epoll events */epoll_data_t data;/* User data variable */};typedef union epoll_data{void*ptr;intfd;uint32_tu32;uint64_tu64;} epoll_data_t;

一般data共同体我们设置其成员fd即可,也就是epoll_ctl函数的第三个参数。

events集合

EPOLLIN

表示对应的文件描述符可以读(包括对端SOCKET正常关闭)

EPOLLOUT

表示对应的文件描述符可以写

EPOLLPRI

表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)

EPOLLERR

表示对应的文件描述符发生错误

EPOLLHUP

表示对应的文件描述符被挂断

EPOLLET

将EPOLL设为边缘触发(EdgeTriggered)模式,这是相对于水平触发(LevelTriggered)来说的

EPOLLONESHOT

只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

/**示例: epoll使用示例 注:client端与测试端与前同, 而且使用相同的测试端测试select/poll/epoll, 可以发现epoll的效率是非常高的**///添加fd到epollvoid addFd(int epollfd, int fd, const uint32_t &events = EPOLLIN, bool et = false){struct epoll_event event;event.events = events;if (et)event.events |= EPOLLET;event.data.fd = fd;if( epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event) == -1 )err_exit("epoll_ctl_add error");}//从epoll删除fdvoid delFd(int epollfd, int fd){struct epoll_event event;event.data.fd = fd;if( epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &event) == -1 )err_exit("epoll_ctl_del error");}int main(){signal(SIGPIPE, sigHandlerForSigPipe);try{TCPServer server(8001);int listenfd = server.getfd();int epollfd = epoll_create1(EPOLL_CLOEXEC);if (epollfd == -1)err_exit("epoll_create1 error");// 将监听套接字注册到epolladdFd(epollfd, listenfd, EPOLLIN, true);// 用于保存epoll_wait返回事件数组std::vector<struct epoll_event> events(16);char buf[BUFSIZ];int count = 0;while (true){// 等待epoll返回int nReady = epoll_wait(epollfd, &*events.begin(), (int)events.size(), -1);if (nReady == -1){if (errno == EINTR)continue;err_exit("epoll_wait error");}if ((size_t)nReady == events.size())events.resize(events.size()*2);for (int i = 0; i < nReady; ++i){// 如果是监听套接字发送了可读事件if (events[i].data.fd == listenfd){int connectfd = accept(listenfd, NULL, NULL);if (connectfd == -1)err_exit("accept error");cout << "accept success…" << endl;cout << "count = " << ++count << endl;setUnBlock(connectfd, true);addFd(epollfd, connectfd, EPOLLIN, true);}// 如果是已连接套接字发生了可读事件else if (events[i].events & EPOLLIN){int connectfd = events[i].data.fd;if (connectfd < 0)continue;memset(buf, 0, sizeof(buf));int ret = readline(connectfd, buf, sizeof(buf)-1);if (ret == -1)err_exit("read-line error");// 如果对端关闭else if (ret == 0){cerr << "client connect closed…" << endl;// 将该套接字同epoll中移除delFd(epollfd, connectfd);close(connectfd);continue;}cout << buf;writen(connectfd, buf, strlen(buf));}}}}catch (const SocketException &e){cerr << e.what() << endl;err_exit("TCPServer error");}}

小结-epoll与select、poll的区别

1.相比于select与poll,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。

“但行好事,莫问前程”,往往成功的几率反而更大些;

Socket编程实践(11)

相关文章:

你感兴趣的文章:

标签云: