从epoll构建muduo-2 最简单的epoll
来源:互联网 发布:懒蛋蛋裙子淘宝 编辑:程序博客网 时间:2024/05/11 03:46
mini-muduo版本传送门
version 0.00 从epoll构建muduo-1 mini-muduo介绍
version 0.01 从epoll构建muduo-2 最简单的epoll
version 0.02 从epoll构建muduo-3 加入第一个类,顺便介绍Reactor
version 0.03 从epoll构建muduo-4 加入Channel
version 0.04 从epoll构建muduo-5 加入Acceptor和TcpConnection
version 0.05 从epoll构建muduo-6 加入EventLoop和Epoll
version 0.06 从epoll构建muduo-7 加入IMuduoUser
version 0.07 从epoll构建muduo-8 加入发送缓冲区和接收缓冲区
version 0.08 从epoll构建muduo-9 加入onWriteComplate回调和Buffer
version 0.09 从epoll构建muduo-10 Timer定时器
version 0.11 从epoll构建muduo-11 单线程Reactor网络模型成型
version 0.12 从epoll构建muduo-12 多线程代码入场
version 0.13 从epoll构建muduo-13 Reactor + ThreadPool 成型
mini-muduo v 0.01版本,这是mini-muduo的第一个版本,整个程序是一个100行的epoll示例
下面粘贴的代码省略了头文件引用,完整可运行的示例可从github下载,使用命令git checkout v0.01可切换到此版本,在线浏览到这里
- #define MAX_LINE 100
- #define MAX_EVENTS 500
- #define MAX_LISTENFD 5
- int createAndListen()
- {
- int on = 1;
- int listenfd = socket(AF_INET, SOCK_STREAM, 0);
- struct sockaddr_in servaddr;
- fcntl(listenfd, F_SETFL, O_NONBLOCK); //no-block io
- setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
- servaddr.sin_family = AF_INET;
- servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
- servaddr.sin_port = htons(11111);
- if(-1 == bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)))
- {
- cout << "bind error, errno:" << errno << endl;
- }
- if(-1 == listen(listenfd, MAX_LISTENFD))
- {
- cout << "listen error, errno:" << errno << endl;
- }
- return listenfd;
- }
- int main(int args, char** argv)
- {
- struct epoll_event ev, events[MAX_EVENTS];
- int listenfd,connfd,sockfd;
- int readlength;
- char line[MAX_LINE];
- struct sockaddr_in cliaddr;
- socklen_t clilen = sizeof(struct sockaddr_in);
- int epollfd = epoll_create(1);
- if (epollfd < 0)
- cout << "epoll_create error, error:" << epollfd << endl;
- listenfd = createAndListen();
- ev.data.fd = listenfd;
- ev.events = EPOLLIN;
- epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev);
- for(;;)
- {
- int fds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
- if(fds == -1)
- {
- cout << "epoll_wait error, errno:" << errno << endl;
- break;
- }
- for(int i = 0; i < fds; i++)
- {
- if(events[i].data.fd == listenfd)
- {
- connfd = accept(listenfd, (sockaddr*)&cliaddr, (socklen_t*)&clilen);
- if(connfd > 0)
- {
- cout << "new connection from "
- << "[" << inet_ntoa(cliaddr.sin_addr)
- << ":" << ntohs(cliaddr.sin_port) << "]"
- << " accept socket fd:" << connfd
- << endl;
- }
- else
- {
- cout << "accept error, connfd:" << connfd
- << " errno:" << errno << endl;
- }
- fcntl(connfd, F_SETFL, O_NONBLOCK); //no-block io
- ev.data.fd = connfd;
- ev.events = EPOLLIN;
- if( -1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev))
- cout << "epoll_ctrl error, errno:" << errno << endl;
- }
- else if(events[i].events & EPOLLIN)
- {
- if((sockfd = events[i].data.fd) < 0)
- {
- cout << "EPOLLIN sockfd < 0 error " << endl;
- continue;
- }
- bzero(line, MAX_LINE);
- if((readlength = read(sockfd, line, MAX_LINE)) < 0)
- {
- if(errno == ECONNRESET)
- {
- cout << "ECONNREST closed socket fd:" << events[i].data.fd << endl;
- close(sockfd);
- }
- }
- else if( readlength == 0)
- {
- cout << "read 0 closed socket fd:" << events[i].data.fd << endl;
- close(sockfd);
- }
- else
- {
- if(write(sockfd, line, readlength) != readlength)
- cout << "error: not finished one time" << endl;
- }
- }
- }
- }
- return 0;
- }
程序说明:
1 使用epoll的三个函数
int epoll_create(int size) 创建一个epoll文件描述符
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) 将socket描述符加入/移出epoll监听,修改注册事件
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout) 在epoll描述符上等待事件的发生,并获得事件
2 epoll编程最基本模型
伪代码
- epollfd = epoll_create(...) //创建epoll描述符
- listenfd = socket(...) //创建用于端口监听的socket
- bind(listenfd ...) //绑定
- listen(listenfd ...) //开始在端口监听
- epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd...) //将listenfd加入epoll描述符监听
- for(;;) //无限循环
- {
- n = epoll_wait(...) //等待epoll描述符的事件发生
- for(0 ~ n) //可能有多个读写事件发生,遍历所有事件
- {
- if(events[i].data.fd == listenfd) //通过发生事件的socket描述符确认有新链接,
- { //而非已经打开的socket的读写事件
- connfd = accpet(...) //accept新连接为connfd
- epoll_ctl(...) //connfd加入epoll监听,像上面listenfd一样
- }
- else if(events[i].events & EPOLLIN) //发生事件的socket不是listenfd而是connfd
- { //且事件集里有EPOLLIN表明有数据要读取
- n = read(...) //读取数据
- if(n == 0) //读到0字节,需要关闭socket
- close(connfd) //close会自动将connfd从epoll监听中删除
- } //无需调用epoll_ctl(..EPOLL_CTL_DEL..)
- else if(...) //其他各种事件处理依次处理
- ... }
- }
4 83行的bzero是从UNP(第一卷P6)里学来的,因为memset的三个参数总是容易搞混。
5 41行和72行虽然只注册了EPOLLIN事件,但是有另外两个事件会自动被注册:EPOLLERR和EPOLLHUP, man epoll_ctl可以看到相关说明。
6 54行和76行根据发生事件的socket描述符结合events里的事件判断应该如何进行下一步处理,如果socket描述符为listenfd,就要accpet新连接,否则发生事件的socket就是connfd,需要判断发生了哪些事件,后续调用read还是write,本例只调用了read
7 89行和95行两处调用了close,后续会详细解释原因
1 read返回-1,且错误码为ECONNRESET。
2 read返回0。
8 10行和70行将socket设置为no-blocking模式,加入epoll的socket要先设置为no-blocking
9 11行中用于监听端口的listenfd被设置为SO_REUSEADDR
10 cout如果链式链调用有可能发生线程切换导致输出被分割,不过目前例子是单线程,暂时忽略这个问题。
- 从epoll构建muduo-2 最简单的epoll
- 从epoll构建muduo-2 最简单的epoll
- 从epoll构建muduo-4 加入Channel
- 从epoll构建muduo-7 加入IMuduoUser
- 从epoll构建muduo-12 多线程入场
- 从epoll构建muduo-10 Timer定时器
- 从epoll构建muduo-4 加入Channel
- 从epoll构建muduo-7 加入IMuduoUser
- 从epoll构建muduo-10 Timer定时器
- 从epoll构建muduo-12 多线程入场
- 从epoll构建muduo-6 加入EventLoop和Epoll
- 从epoll构建muduo-6 加入EventLoop和Epoll
- 从epoll构建muduo-1 mini-muduo介绍
- 从epoll构建muduo-1 mini-muduo介绍
- 从epoll构建muduo-5 加入Acceptor和TcpConnection
- 从epoll构建muduo-9 加入onWriteComplate回调和Buffer
- 从epoll构建muduo-13 Reactor + ThreadPool 成型
- 从epoll构建muduo-5 加入Acceptor和TcpConnection
- SDUTOJ3361-数据结构实验之图论四:迷宫探索
- Vector c++ 的基本操作 矢量 *_* !
- 十、cocos2d-x 字体描边和制作阴影
- Xcode中修改变量名、类名及字符串的替换操作
- 搭建Eclipse+Tomcat开发环境
- 从epoll构建muduo-2 最简单的epoll
- iOS本地化 NSLocalizedString的使用
- 在 MongoDB 中执行两阶段提交
- linux shell自带变量
- Oracle数据库的逻辑结构未完待续、、、
- C++ Timer定时器
- Inno Setup制作安装包程序简单示例
- NSString和NSMutableString整理与总结
- RxJava教程(二)