想快速了解协程与网络调用的原来么,那么请赶紧关闭本页,因为下面都是在扯淡。

这几天是端午假期,第一天大算照着网上一大堆基于ucontext来写协程的文章自己也写一个简单的协程实现。于是第一天我就开始动手了,非常轻松愉快,毕竟是抄么。但是很多文章写到怎么用ucontext做切换就戛然而止了,很显然在我们日常协程用的比较多的网络应用中没有人会去做手工的协程切换,这些协程的切换调度其实都被封装在socket接口中,即在协程进行网络操作需要等待时调用yield使得当前协程挂起,在等待的时间到达时调用resume恢复已挂起的协程。这样我们就可以用同步的编程模式写出异步的网络程序。所以我打算把这一步也实现一下,当然这里比起用ucontext来讲还是麻烦一些,在结合epoll实现同步阻塞的read和write时调了一些时间。

在一天之内没有写完,假期么,早点回去看看电影也是应该的,顺便买点饮料饼干准备过夜。手上拿的东西比较多,进出租房的时候,一阵风把还没来得及关上的门‘喷’的一吹,过道里就传来东北房东的喊声。寄人篱下,又是深夜我知道一顿教训是没逃了,结果这逼没玩没了,我就不能忍了,搞得我故意关的,亏我平时进出都小心翼翼的都不是关而是把门‘合’上的。结果自然是我得搬走,毕竟房子是人家的。其实我倒是不太气,因为虽然这离公司不过两百米,但不到6平米的空间还包括了卫生间,这房东又住在出租的套间里,还时不时的抽烟,原来想着合同签了要反悔押金就要损失了。这回倒好,让我走,行,把押金退了就好,这货也答应。所以这个结果不知是双赢还是双输。

端午的第二天我就破天荒的走了2.5万步,奔波找房子,其实我是比较屌丝就是不想花太多钱在房租上,如果肯出个两千房子随便找,一大把。原来还不敢直接打出租房前贴的小广告电话,结果那天一口气打了二三十个,脾气好的没房子也会跟你说一句,脾气差的直接挂电话。这时候想想以前和同学一起租个套间真是好太多了,房东人也不错。

三天假期突然就到第三天了,先把租房的事情放一放,大不了睡公司,房子总是会有的,不就是个钱的问题。这天就用异步和协程切换把socket的同步阻塞读写给实现了一下,并且写了个echo server像是网络编程界的helloworld。好了talk is cheap,shou you the code!(需要使用-std=c++11编译选项)

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <time.h>
  5. #include <unistd.h>
  6. #include <ucontext.h>
  7. #include <sys/types.h>
  8. #include <sys/socket.h>
  9. #include <sys/epoll.h>
  10. #include <arpa/inet.h>
  11. #include <netdb.h>
  12. #include <fcntl.h>
  13. #include <errno.h>
  14. #include <map>
  15. #include <list>
  16. #define MAX_ROUTINE 256
  17. #define MAX_STACK_SIZE_KB 128
  18. #define MAX_EVENT_SIZE 10240
  19. enum { UNUSED = 0, IDLE = 1, RUNNING = 2 };
  20. typedef void (*STD_ROUTINE_FUNC)(void);
  21. typedef struct {
  22. ucontext_t ctx;
  23. char stack[ MAX_STACK_SIZE_KB * 1024 ];
  24. STD_ROUTINE_FUNC func;
  25. int status;
  26. int wait_fd;
  27. int events;
  28. } RoutineContext;
  29. typedef std::map<unsigned int, std::list<int> > TimeoutMap;
  30. typedef struct {
  31. struct epoll_event events[MAX_ROUTINE];
  32. RoutineContext routines[MAX_ROUTINE];
  33. TimeoutMap timeout_map;
  34. ucontext_t main;
  35. int epoll_fd;
  36. int running_id;
  37. int routine_cnt;
  38. } RoutineCenter;
  39. RoutineCenter routinecenter;
  40. void init() {
  41. srand(time(NULL));
  42. routinecenter.running_id = -1;
  43. }
  44. void routine_wrap() {
  45. int running_id = routinecenter.running_id;
  46. if ( running_id < 0 ) {
  47. puts("current context don't attach to any routine except main.");
  48. return;
  49. }
  50. routinecenter.routines[running_id].func();
  51. routinecenter.routines[running_id].status = UNUSED;
  52. routinecenter.routine_cnt--;
  53. }
  54. int create( STD_ROUTINE_FUNC routine_proc) {
  55. int new_id = -1;
  56. for (int i = 0; i < MAX_ROUTINE; i++) {
  57. if ( routinecenter.routines[i].status == UNUSED ) {
  58. new_id = i;
  59. break;
  60. }
  61. }
  62. if ( new_id < 0 ) {
  63. puts("max routine number reached. no more routine.");
  64. return -1;
  65. }
  66. ucontext_t* pctx = &(routinecenter.routines[new_id].ctx);
  67. getcontext(pctx);
  68. pctx->uc_stack.ss_sp = routinecenter.routines[new_id].stack;
  69. pctx->uc_stack.ss_size = MAX_STACK_SIZE_KB * 1024;
  70. pctx->uc_stack.ss_flags = 0;
  71. pctx->uc_link = &(routinecenter.main);
  72. makecontext(pctx, routine_wrap, 0);
  73. routinecenter.routines[new_id].status = IDLE;
  74. routinecenter.routines[new_id].func = routine_proc;
  75. routinecenter.routine_cnt++;
  76. return new_id;
  77. }
  78. int yield() {
  79. if ( routinecenter.running_id < 0 ) {
  80. puts("no routine running except main.");
  81. return 0;
  82. }
  83. int running_id = routinecenter.running_id;
  84. RoutineContext* info = &(routinecenter.routines[running_id]);
  85. info->status = IDLE;
  86. info->events = 0;
  87. swapcontext( &(info->ctx), &(routinecenter.main) );
  88. return 0;
  89. }
  90. int resume(int id, int events = 0) {
  91. if ( id < 0 || id >= MAX_ROUTINE ) {
  92. puts("routine id out of bound.");
  93. return -1;
  94. }
  95. int running_id = routinecenter.running_id;
  96. if (id == running_id) {
  97. puts("current routine is running already.");
  98. return 0;
  99. }
  100. if (routinecenter.routines[id].status != IDLE) {
  101. puts("target routine is not in idel status. can't resume");
  102. return -1;
  103. }
  104. routinecenter.running_id = id;
  105. routinecenter.routines[id].status = RUNNING;
  106. routinecenter.routines[id].events = events;
  107. if (running_id < 0) {
  108. // in main
  109. swapcontext( &(routinecenter.main), &(routinecenter.routines[id].ctx));
  110. routinecenter.running_id = -1;
  111. } else {
  112. // in other routine
  113. routinecenter.routines[running_id].status = IDLE;
  114. swapcontext( &(routinecenter.routines[running_id].ctx), &(routinecenter.routines[id].ctx) );
  115. routinecenter.running_id = running_id;
  116. }
  117. return 0;
  118. }
  119. int routine_id() { return routinecenter.running_id; }
  120. void set_nonblocking(int fd) {
  121. int flags = fcntl(fd, F_GETFL, 0);
  122. int ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
  123. if (ret < 0) {
  124. perror("set nonblocking fail.");
  125. exit(-1);
  126. }
  127. }
  128. void mod_event(int fd, int events, int op, int routine_id) {
  129. struct epoll_event ev = {0};
  130. if ( EPOLL_CTL_DEL != op ) {
  131. ev.data.fd= routine_id;
  132. routinecenter.routines[routine_id].wait_fd = fd;
  133. }
  134. ev.events = events;
  135. int ret = epoll_ctl(routinecenter.epoll_fd, op, fd, &ev);
  136. if (ret < 0) {
  137. if ( errno == EEXIST && op != EPOLL_CTL_DEL) {
  138. epoll_ctl(routinecenter.epoll_fd, EPOLL_CTL_MOD, fd, &ev);
  139. }
  140. }
  141. }
  142. int routine_read(int fd, char* buff, int size) {
  143. mod_event(fd, EPOLLIN, EPOLL_CTL_ADD, routine_id());
  144. while (!(routinecenter.routines[routine_id()].events & EPOLLIN)) {
  145. yield();
  146. }
  147. while (routinecenter.routines[routine_id()].events & EPOLLIN) {
  148. int need = size;
  149. int readin = 0;
  150. while (need > 0) {
  151. int ret = read(fd, buff + readin, need);
  152. if (ret <= 0) {
  153. break;
  154. } else {
  155. readin += ret;
  156. need -= ret;
  157. }
  158. }
  159. if (readin == 0 && size != 0) {
  160. yield();
  161. continue;
  162. } else {
  163. mod_event(fd, EPOLLIN, EPOLL_CTL_DEL, routine_id());
  164. }
  165. return readin;
  166. }
  167. printf("routine[%d][%s]routine system ran out of order.\n", routine_id(), __func__);
  168. return 0;
  169. }
  170. int routine_write(int fd, char* buff, int size) {
  171. mod_event(fd, EPOLLOUT, EPOLL_CTL_ADD, routine_id());
  172. while (!(routinecenter.routines[routine_id()].events & EPOLLOUT)) {
  173. yield();
  174. }
  175. while (routinecenter.routines[routine_id()].events & EPOLLOUT) {
  176. int need = size;
  177. int wrout = 0;
  178. while (need > 0) {
  179. int ret = write(fd, buff + wrout, need);
  180. if (ret <= 0) {
  181. break;
  182. } else {
  183. wrout += ret;
  184. need -= ret;
  185. }
  186. }
  187. if ( wrout == 0 && size != 0 ) {
  188. yield();
  189. continue;
  190. } else {
  191. mod_event(fd, EPOLLOUT, EPOLL_CTL_DEL, routine_id());
  192. }
  193. return wrout;
  194. }
  195. printf("routine[%d][%s]routine system ran out of order.\n", routine_id(), __func__);
  196. return 0;
  197. }
  198. void routine_delay_resume(int rid, int delay_sec) {
  199. if (delay_sec <= 0) {
  200. resume(rid);
  201. return;
  202. }
  203. routinecenter.timeout_map[time(NULL) + delay_sec].push_back(rid);
  204. }
  205. void routine_sleep(int time_sec) {
  206. routine_delay_resume(routine_id(), time_sec);
  207. yield();
  208. }
  209. int routine_nearest_timeout() {
  210. if (routinecenter.timeout_map.empty()) {
  211. return 60 * 1000; // default epoll timeout
  212. }
  213. unsigned int now = time(NULL);
  214. int diff = routinecenter.timeout_map.begin()->first - now;
  215. return diff < 0 ? 0 : diff * 1000;
  216. }
  217. void routine_resume_timeout() {
  218. // printf("[epoll] process timeout\n");
  219. if ( routinecenter.timeout_map.empty() ) {
  220. return;
  221. }
  222. unsigned int timestamp = routinecenter.timeout_map.begin()->first;
  223. if (timestamp > time(NULL)) {
  224. return;
  225. }
  226. std::list<int>& routine_ids = routinecenter.timeout_map.begin()->second;
  227. for (int i : routine_ids) {
  228. resume(i);
  229. }
  230. routinecenter.timeout_map.erase(timestamp);
  231. }
  232. void routine_resume_event(int n) {
  233. // printf("[epoll] process event\n");
  234. for (int i = 0; i < n; i++) {
  235. int rid = routinecenter.events[i].data.fd;
  236. resume(rid, routinecenter.events[i].events);
  237. }
  238. }
  239. void create_routine_poll() {
  240. routinecenter.epoll_fd = epoll_create1 (0);
  241. if (routinecenter.epoll_fd == -1) {
  242. perror ("epoll_create");
  243. exit(-1);
  244. }
  245. }
  246. void routine_poll() {
  247. for (;;) {
  248. int n = epoll_wait (routinecenter.epoll_fd, routinecenter.events, MAX_EVENT_SIZE, routine_nearest_timeout());
  249. // printf("[epoll] event_num:%d\n", n);
  250. routine_resume_timeout();
  251. routine_resume_event(n);
  252. }
  253. }
  254. void echo_server_routine() {
  255. int conn_fd = routinecenter.routines[routine_id()].wait_fd;
  256. printf("routine[%d][%s] server start. conn_fd: %d\n", routine_id(), __func__, conn_fd);
  257. for (;;) {
  258. printf("routine[%d][%s] loop start. conn_fd: %d\n", routine_id(), __func__, conn_fd);
  259. char buf[512] = {0};
  260. int n = 0;
  261. n = routine_read( conn_fd, buf, sizeof (buf) );
  262. if (n < 0) {
  263. perror("server read error.");
  264. break;
  265. }
  266. buf[n] = '\0';
  267. printf("routine[%d][%s] server read: %s", routine_id(), __func__, buf);
  268. unsigned int in_ts = time(NULL);
  269. routine_sleep(1);
  270. unsigned int out_ts= time(NULL);
  271. char obuf[512] = {0};
  272. snprintf(obuf, sizeof(obuf), "%s rev_ts:%u sent_ts:%u\n", buf, in_ts, out_ts);
  273. printf("routine[%d][%s] server write: %s", routine_id(), __func__, obuf);
  274. n = routine_write(conn_fd, obuf, strlen(obuf) + 1);
  275. if (n < 0) {
  276. perror("server write error.");
  277. break;
  278. }
  279. }
  280. printf("routine[%d][%s] server start. conn_fd: %d\n", routine_id(), __func__, conn_fd);
  281. }
  282. void request_accept() {
  283. for (;;) {
  284. struct sockaddr_in addr = {0};
  285. socklen_t slen = sizeof(addr);
  286. int fd = accept(routinecenter.routines[routine_id()].wait_fd, (struct sockaddr*)&addr, &slen);
  287. struct sockaddr_in peer = {0};
  288. int ret = getpeername(fd, (struct sockaddr*)&peer, &slen);
  289. if (ret < 0) {
  290. perror("getpeername error.");
  291. exit(-1);
  292. }
  293. printf("routine[%d][%s] accept from %s conn_fd:%d\n", routine_id(), __func__, inet_ntoa(peer.sin_addr), fd);
  294. set_nonblocking(fd);
  295. int rid = create( echo_server_routine );
  296. routinecenter.routines[rid].wait_fd = fd;
  297. mod_event(fd, EPOLLIN, EPOLL_CTL_ADD, rid);
  298. resume(rid);
  299. yield();
  300. }
  301. }
  302. void bind_listen(unsigned short port) {
  303. int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
  304. struct sockaddr_in addr = {0};
  305. addr.sin_family = AF_INET;
  306. addr.sin_port = htons(port);
  307. addr.sin_addr.s_addr = INADDR_ANY;
  308. int ret = bind( listen_fd, (struct sockaddr*)&addr, sizeof(struct sockaddr) );
  309. if (ret < 0) {
  310. perror("bind fail.");
  311. exit(-1);
  312. }
  313. ret = listen( listen_fd, 20 );
  314. if (ret < 0) {
  315. perror("listen fail.");
  316. exit(-1);
  317. }
  318. printf("routine[%d] listen bind at port: %u\n", routine_id(), port);
  319. set_nonblocking( listen_fd );
  320. int rid = create( request_accept );
  321. mod_event( listen_fd, EPOLLIN, EPOLL_CTL_ADD, rid );
  322. }
  323. int main() {
  324. init();
  325. create_routine_poll();
  326. bind_listen(55667);
  327. bind_listen(55668);
  328. bind_listen(55669);
  329. routine_delay_resume(create( [](){ printf("routine[%d] alarm fired\n", routine_id()); } ), 3);
  330. routine_poll();
  331. puts("all routine exit");
  332. return 0;
  333. }

协程与Epoll的配合的更多相关文章

  1. Python之路-python(Queue队列、进程、Gevent协程、Select\Poll\Epoll异步IO与事件驱动)

    一.进程: 1.语法 2.进程间通讯 3.进程池 二.Gevent协程 三.Select\Poll\Epoll异步IO与事件驱动 一.进程: 1.语法 简单的启动线程语法 def run(name): ...

  2. 多进程、协程、事件驱动及select poll epoll

    目录 -多线程使用场景 -多进程 --简单的一个多进程例子 --进程间数据的交互实现方法 ---通过Queues和Pipe可以实现进程间数据的传递,但是不能实现数据的共享 ---Queues ---P ...

  3. Python学习笔记整理总结【网络编程】【线程/进程/协程/IO多路模型/select/poll/epoll/selector】

    一.socket(单链接) 1.socket:应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socke ...

  4. Python 协程/异步IO/Select\Poll\Epoll异步IO与事件驱动

    1 Gevent 协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到 ...

  5. python之协程与IO操作

    协程 协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B ...

  6. python学习道路(day11note)(协程,同步与异步的性能区别,url爬网页,select,RabbitMq)

    1.协程 #协程 又称微线程 是一种用户的轻量级线程 程序级别代码控制 就不用加机器 #不同函数 = 不同任务 A函数切到B函数没有进行cpu级别的切换,而是程序级别的切换就是协程 yelied #单 ...

  7. Python开发【第九章】:线程、进程和协程

    一.线程 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务 1.t ...

  8. 关于协程的学习 & 线程栈默认10M

    先看的这篇文章:http://blog.csdn.net/qq910894904/article/details/41699541 以nginx为代表的事件驱动的异步server正在横扫天下,那么事件 ...

  9. python 自动化之路 day 10 协程、异步IO、队列、缓存

    本节内容 Gevent协程 Select\Poll\Epoll异步IO与事件驱动 RabbitMQ队列 Redis\Memcached缓存 Paramiko SSH Twsited网络框架 引子 到目 ...

随机推荐

  1. xampp运行MySQL shutdown unexpectedly解决方案

    昨天晚上自己的网站突然打不开了,以为被人黑了.想想不应该啊,这小站不会有人关注的,于是登录服务器看了下,发现是Mysql打不开了 很奇怪,因为今天白天还是可以打开的,下班后也没有碰过服务器 首先看看是 ...

  2. ScrollView嵌套子View的getDrawingCache为空的解决方法

    ScrollView嵌套子View的getDrawingCache为空的解决方法 问题 将组件的显示布局改为可以滚动的,然后用ScrollView作为了View的父类,发现View的getDrawin ...

  3. HTTP 协议中 GET 和 POST 方法详解

    GET请求报文分析 1.请求行 请求方法 GET(描述该请求采用了什么请求方法),HTTP 1.0 和 1.1 协议中共包含10种请求方法.不过 HTTP 1.1 中只有8种方法. URI 请求WEB ...

  4. Redis 配置内容总结

    命令 Redis 的配置文件位于 Redis 安装目录下,文件名为 redis.conf. 你可以通过 CONFIG 命令查看或设置配置项. (1)config get config_setting_ ...

  5. HttpServlet的转发和重定向

    HttpServletResponse重定向 1.HTTP协议规定了一种重定向的机制,重定向的运作流程如下 用户在浏览器输入特定的URL,请求访问服务端的某个组件. 服务端的组件返回一个状态码为302 ...

  6. Day8:html和css

    Day8:html和css 显示和隐藏: display: none 为 无,隐藏元素 display: block 为 显示元素 转换为块级元素 visibility: visible 显示 vis ...

  7. 第58节:Java中的图形界面编程-GUI

    欢迎到我的简书查看我的文集 前言: GUI是图形用户界面,在Java中,图形用户界面我们用GUI表示,而GUI的完整英文为: Graphical User Interface(图形用户接口), 所谓图 ...

  8. gdb调试的layout使用

    layout:用于分割窗口,可以一边查看代码,一边测试.主要有以下几种用法:layout src:显示源代码窗口layout asm:显示汇编窗口layout regs:显示源代码/汇编和寄存器窗口l ...

  9. 机器学习入门 - Google的机器学习速成课程

    1 - MLCC 通过机器学习,可以有效地解读数据的潜在含义,甚至可以改变思考问题的方式,使用统计信息而非逻辑推理来处理问题. Google的机器学习速成课程(MLCC,machine-learnin ...

  10. os模块及其API&属性

    模块: os os.path 所包含API列表: os.uname: 获取详细的系统信息 os.rename: 文件重命名 os.remove: 删掉文件 os.mkdir: 创建一个目录 os.rm ...