epoll的实现与深入思考
提契
纸上得来终觉浅,绝知此事要躬行。
正文
前段时间写了一篇epoll的学习文章,但没有自己的心得总觉得比较肤浅,花了一些时间补充一个epoll的实例,并浅析一下过程中遇到的问题。
上epoll_server的例子,epoll的代码都在这里
1 #include<iostream>
2 #include<stdlib.h>
3 #include<sys/epoll.h>
4 #include<sys/socket.h>
5 #include<netinet/in.h>
6 #include<sys/types.h>
7 #include<fcntl.h>
8
9 using namespace std;
10 const int PORT = 8888;
11 const int MAX_CLIENT_NUM = 10000;
12 const int MAX_LEN = 2000;
13
14 bool setfdnoblock(int fd)
15 {
16 int flg = fcntl(fd, F_GETFL);
17 if(flg < 0)
18 {
19 cout << "get fd flag failed" << endl;
20 return false;
21 }
22 if(fcntl(fd, F_SETFL, O_NONBLOCK | flg) < 0)
23 {
24 return false;
25 }
26 return true;
27 }
28
29 int CreateTcpServer(int port, int listennum)
30 {
31 int fd;
32 fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
33
34 sockaddr_in TcpServer;
35 bzero(&TcpServer, sizeof(TcpServer));
36 TcpServer.sin_family = AF_INET;
37 TcpServer.sin_port = htons(8888);
38 TcpServer.sin_addr.s_addr = htonl(INADDR_ANY);
39
40 int iRet = bind(fd, (struct sockaddr*)&TcpServer, sizeof(TcpServer));
41 if(-1 == iRet)
42 {
43 cout << "server bind error!" << endl;
44 return -1;
45 }
46 if(listen(fd, listennum) == -1)
47 {
48 cout << "server listen error" << endl;
49 return -1;
50 }
51 return fd;
52 }
53
54 int main()
55 {
56 int Serverfd = CreateTcpServer(PORT, MAX_CLIENT_NUM);
57 if(Serverfd == -1)
58 {
59 cout << "server create failed" << endl;
60 }
61 else
62 {
63 cout << "serverfd is :" << Serverfd << endl;
64 }
65
66 int Epollfd = epoll_create(MAX_CLIENT_NUM);
67 if(Epollfd == -1)
68 {
69 cout << "epoll_create failed" << endl;
70 }
71 epoll_event ev, events[MAX_CLIENT_NUM];
72 int nfds = 0;
73 int client = 0;
74 char buff[MAX_LEN];
75 sockaddr_in CliAddr;
76 unsigned int iCliSize = sizeof(CliAddr);
77 ev.events = EPOLLIN|EPOLLOUT;
78 ev.data.fd = Serverfd;
79 if(!setfdnoblock(Serverfd))
80 {
81 cout << "set serverfd no_block failed" << endl;
82 }
83 if(epoll_ctl(Epollfd, EPOLL_CTL_ADD, Serverfd, &ev))
84 {
85 cout << "epoll add serverfd error" << endl;
86 }
87 while(1)
88 {
89 nfds = epoll_wait(Epollfd, events, MAX_CLIENT_NUM, 100000);
90 if(nfds == -1)
91 {
92 cout << "error occur, exit" << endl;
93 return -1;
94 }
95 else if( nfds == 0)
96 {
97 cout << "epoll_wait return zero" << endl;
98 }
99 else
100 {
101 for(int i = 0; i < nfds; i++)
102 {
103 cout << "events[i].data.fd is :" << events[i].data.fd << endl;
104 if(events[i].data.fd == Serverfd)
105 {
106 cout << " Serverfd received event" << endl;
107 client = accept(Serverfd, (struct sockaddr*)&CliAddr, &iCliSize);
108 if(client == -1)
109 {
110 cout << "accept error" << endl;
111 return -1;
112 }
113 ev.data.fd = client;
114 if(!setfdnoblock(client))
115 {
116 cout << "set client fd no_block error" << endl;
117 }
118 if(epoll_ctl(Epollfd, EPOLL_CTL_ADD, client, &ev))
119 {
120 cout << "epoll add client error" << endl;
121 }
122 else
123 {
124 cout << "success add client" << endl;
125 }
126 }
127 else if(events[i].events&EPOLLIN)
128 {
129 cout << "recv client msg" << endl;
130 if(events[i].data.fd < 0)
131 {
132 cout << " event[i].data.fd is smaller than zero" << endl;
133 continue;
134 }
135 if(read(events[i].data.fd, buff, MAX_LEN) == -1)
136 {
137 perror("clifd read");
138 }
139 else
140 {
141 cout << "read client msg suc" << endl;
142 printf("%s",buff);
143 }
144 char resp[] = "recv a client msg, this is resp msg";
145 write(events[i].data.fd, resp, strlen(resp)+1);
146 //read and mod
147 }
148 else if(events[i].events&EPOLLOUT)
149 {
150 //send and mod
151 }
152 }
153 }
154 }
155 }
下面是一个TCP的client例子
1 #include<sys/types.h>
2 #include<sys/socket.h>
3 #include<netinet/in.h>
4 #include<stdlib.h>
5 #include<arpa/inet.h>
6 #include<stdio.h>
7 #include<string.h>
8 #include<iostream>
9
10 using namespace std;
11 const int MAX_BUFF=2048;
12 char notify[] = "i'm client";
13
14 int main()
15 {
16 int Clifd;
17 char buff[MAX_BUFF];
18 Clifd = socket(AF_INET, SOCK_STREAM, 0);
19 if(Clifd == -1)
20 {
21 perror("clifd socket");
22 }
23 sockaddr_in CliSock;
24 bzero(&CliSock, sizeof(CliSock));
25 CliSock.sin_family = AF_INET;
26 CliSock.sin_addr.s_addr = inet_addr("203.195.243.17");
27 CliSock.sin_port = htons(8888);
28
29 if(-1 == connect(Clifd, (struct sockaddr*)&CliSock, sizeof(CliSock)))
30 {
31 perror("clifd connect");
32 }
33 if(write(Clifd, notify, strlen(notify)+1) == -1)
34 {
35 perror("clifd write");
36 }
37 cout << "write over" << endl;
38 if(read(Clifd, buff, MAX_BUFF) == -1)
39 {
40 perror("clifd read");
41 }
42 else
43 {
44 cout << buff << endl;
45 }
46 }
比较简单的代码,算是epoll的典型框架,不再解释代码,将编码过程犯得几个错拿出来供大家借鉴:
1.epoll_ctl(Epollfd, EPOLL_CTL_ADD, Serverfd, &ev)添加Serverfd至epoll监视列表时ev.data.fd并没有设置等于serverfd,结果客户端来连接的时候不停收到EPOLLIN消息,但events[i].data.fd == Serverfd却从不成立,在epoll_ctl之前添加ev.data.fd = Serverfd解决问题,对此我的理解是epoll_ctl将第三参数Serverfd加入监视列表,并将ev结构体与Serverfd关联起来,但epoll不会主动监测和修改ev的其他参数,在epoll_wait时epoll监测到Serverfd上有可读的消息就返回,并将Serverfd关联的ev结构体赋值给events这个事件列表,所以events[i].data.fd其实就是epoll_ctl注册时的ev.data.fd。
2.当client代码调用connect以后立即返回了连接建立成功的结果,此时epoll_server的accept函数还没有调用,那么TCP的连接建立原理是怎样的?
从这张图片来看,TCP的server端在listen以后会被动接受client的请求并建立连接,accept和建立连接没有直接关系,accept只是从serverfd上获取到请求建连的client信息,accept之前已经完成了链路的创建。
延伸话题
同步/异步与阻塞/非阻塞
同步/异步
同步和异步往往和线程有关系,比如SendMsg,线程A->线程B,发送消息后需要等待其他线程或进程的响应。
如果SendMsg以后线程A等待线程B返回响应消息,线程才继续处理,这就是同步
如果SendMsg以后线程A就继续做自己的事情,而注册了一个回调或者响应线程来处理线程B的响应消息,这就是异步
同步往往是阻塞的
异步往往是非阻塞的
阻塞/非阻塞
阻塞read:线程将阻塞,直到有可读的数据。
非阻塞read:设置O_NOBLOCK以后,如果fd没有可读的数据,read将立即返回-1并设errno为EAGAIN。
在有可读数据时阻塞与非阻塞read是一样的。
阻塞write:data从user-mode空间move到kernel-mode空间,之后系统完成kernel-mode到物理缓冲的处理并返回,然后阻塞IO返回。
非阻塞write:data从user-mode空间move到kernel-mode空间,write返回。
小结
epoll的延伸应用、同步异步、阻塞非阻塞都可以谈很多,以后写对应的文章进行详解。
补充
腾讯云服务器真是一台裸机,ftp服务没有打开,默认只能ssh登陆root账户,man手册用法有点奇怪,用了一天以后还报了hostname错误和eth1找不到网络设备,感觉不是很成熟的产品。
epoll的实现与深入思考的更多相关文章
- 关于Android中为什么主线程不会因为Looper.loop()里的死循环卡死?引发的思考,事实可能不是一个 epoll 那么 简单。
( 转载请务必标明出处:http://www.cnblogs.com/linguanh/, 本文出自:[林冠宏(指尖下的幽灵)的博客]) 前序 本文将会把一下三个问题阐述清楚以及一个网上的普遍观点的补 ...
- (转)关于Android中为什么主线程不会因为Looper.loop()里的死循环卡死?引发的思考,事实可能不是一个 epoll 那么 简单。
( 转载请务必标明出处:http://www.cnblogs.com/linguanh/, 本文出自:[林冠宏(指尖下的幽灵)的博客]) 前序 本文将会把一下三个问题阐述清楚以及一个网上的普遍观点的补 ...
- 知识联结梳理 : I/O多路复用、EPOLL(SELECT/POLL)、NIO、Event-driven、Reactor模式
为了形成一个完整清晰的认识,将概念和关系梳理出来,把坑填平. I/O多路复用 I/O多路复用主要解决传统I/O单线程阻塞的问题.它通过单线程管理多个FD,当监听的FD有状态变化的时候的,调用回调函数, ...
- Linux Network IO Model、Socket IO Model - select、poll、epoll
目录 . 引言 . IO机制简介 . 阻塞式IO模型(blocking IO model) . 非阻塞式IO模型(noblocking IO model) . IO复用式IO模型(IO multipl ...
- 分布式MySQL集群方案的探索与思考
转载:http://www.infoq.com/cn/articles/exploration-of-distributed-mysql-cluster-scheme?utm_campaign=rig ...
- Linux下的I/O复用与epoll详解
前言 I/O多路复用有很多种实现.在linux上,2.4内核前主要是select和poll,自Linux 2.6内核正式引入epoll以来,epoll已经成为了目前实现高性能网络服务器的必备技术.尽管 ...
- 【转载】nginx 并发数问题思考:worker_connections,worker_processes与 max clients
注:这个文章主要是作者一直在研究nginx作为http server和反向代理服务器时候所谓最大的max_clients和 worker_connections的计算公式, 其实最后的结论也没有卡上公 ...
- I/O多路复用之epoll
1.select.poll的些许缺点 先回忆下select和poll的接口 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set ...
- Linux编程之epoll
现在有这么一个场景:我是一个很忙的大老板,我有100个手机,手机来信息了,我的秘书就会告诉我"老板,你的手机来信息了."我很生气,我的秘书就是这样子,每次手机来信息就只告诉我来信息 ...
随机推荐
- 【剑指Offer】26、二叉搜索树与双向链表
题目描述: 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 解题思路: 首先要理解此题目的含义,在双向链表中,每个结 ...
- greenplum数据迁移
源集群: 登录集群su - gpadminpsql -d postgres查询数据库信息\l查询用户信息\du 备份需要迁移的库到指定目录pg_dump -C testdata > /home/ ...
- Golang - 处理字符串
目录 Golang - 处理字符串 1. 字符串操作 2. 字符串转换 Golang - 处理字符串 1. 字符串操作 func Contains(s, substr string) bool 字符串 ...
- eclipsephp的环境设置
因需要研究开发opencart的插件模块,需要一套开发php的环境.这类程序就环境配置就够折腾吐血.自认为最简单的方法: 1.下载easyphp安装,这个玩意简单快捷傻瓜.米是5.38: 2.去ecl ...
- Spring Cloud-hystrix Dashboard(八)
单机模式 1.创建一个dashboard项目 2.引入依赖 <!--histrix依赖--> <dependency> <groupId>org.springfra ...
- 数位dp无前导零
题目链接:http:// www.lydsy.com/JudgeOnline/problem.php?id=1026 #include <iostream> #include < ...
- Wireshark中的一些SNMP相关的过滤器
Wireshark中的一些SNMP相关的过滤器 转自 http://linmingren2003.blog.163.com/blog/static/567510032011419825097/ 由 ...
- JVM基础(二) 实现自己的ClassLoader
为何要花时间实现自己的ClassLoader 尽管人生的乐趣非常大一部分来自于将时间花在有意思可是无意义的事情上,可是这件事绝对是有意思并且有意义的,有下面几个情景是值得我们花费时间实现自己的clas ...
- 《编程导论(Java)·2.1.2 啊,我看到了多态》-什么是多态(polymorphism)
1.不明觉厉 很多人学习多态时,会认为. 之所以不明觉厉,由于多态的定义:事物存在的多种表现形态:而后,有人将重载(overload).改写(override).多态变量和泛型归结于同一个术语&quo ...
- 最简单的基于FFmpeg的移动端样例:IOS 推流器
===================================================== 最简单的基于FFmpeg的移动端样例系列文章列表: 最简单的基于FFmpeg的移动端样例:A ...