Chinaunix

标题: Non-blocking下使用write发送socket数据 [打印本页]

作者: vesontio    时间: 2015-04-08 15:31
标题: Non-blocking下使用write发送socket数据
本帖最后由 vesontio 于 2015-04-08 15:53 编辑

我在网上看关于non-blocking的socket发送数据的文章,很多都自己另外实现一个writen函数:

  1. int writen(int sock, unsigned char *buff, size_t len) {
  2.   size_t total, offset;
  3.   ssize_t ret;

  4.   offset = 0;
  5.   total = len;

  6.   while (total > 0) {
  7.     ret = write (fd, buff + offset, total)
  8.       if (ret < 0) {
  9.         if (errno == EAGAIN || errno == EWOULDBLOCK) {
  10.           continue;
  11.         }
  12.         else {
  13.           return -1;
  14.         }
  15.       }
  16.     total -= ret;
  17.     offset += ret;
  18.   }
  19.   return 0;
  20. }
复制代码
上面这段代码没测试过,但是大概就是这个逻辑。

基本说白了就是反复发,直到把 buff 彻底发出去为止。那么问题是:这种做法,与 blocking 何异?反正也是抓着这个 fd 不放,使劲发。
采用 non-blocking 本来就是想提高系统的反应能力吧,这样不发完就不让走的形式,会不会和初衷背道而驰?

作者: windoze    时间: 2015-04-08 15:35
你不这么写不就完了?你就不能在那个循环中顺便再干点别的?
作者: yulihua49    时间: 2015-04-08 15:58
本帖最后由 yulihua49 于 2015-04-08 16:06 编辑
vesontio 发表于 2015-04-08 15:31
我在网上看关于non-blocking的socket发送数据的文章,很多都自己另外实现一个writen函数:上面这段代码没测 ...

  1. int SendNet(int socket,char *buf,int n,int MTU)
  2. {
  3. int bcount,bw;
  4. int sz,i=0;
  5. int fflag;
  6. size_t SendSize;

  7.         if(socket<0) return SYSERR;
  8.         fflag=fcntl(socket,F_GETFL,0);
  9.         if(fflag != -1) fcntl(socket,F_SETFL,fflag|O_NONBLOCK); //异步操作
  10.         bcount=0;
  11.         bw=0;
  12.         if(MTU>500) SendSize=MTU;
  13.         else SendSize=n;
  14.         while(bcount<n){
  15.                 sz=MIN(n-bcount,SendSize);
  16.                 if((bw=write(socket,buf,sz))>0){
  17.                         bcount+=bw;
  18.                         buf+=bw;
  19.                 }
  20.                 if(bw<0&&errno!=EAGAIN) {
  21.                         ShowLog(1,"%s:err=%d,%s",__FUNCTION__,errno,strerror(errno));
  22.                         break;
  23.                 }
  24.                 if(bw < sz && fflag != -1) { //切换任务
  25. ShowLog(5,"%s:tid=%lX,socket=%d,yield bw=%d/%d",__FUNCTION__,pthread_self(),socket,bw,sz);
  26.                     i=do_event(socket,1,0); //yield by EPOLLOUT
  27.                     if(i<0) {//逃跑失败
  28.                         fcntl(socket,F_SETFL,fflag);
  29.                         fflag = -1;
  30.                     }
  31.                 }
  32.         }
  33.         if(fflag != -1)  fcntl(socket,F_SETFL,fflag);
  34.         return bcount==0?-1:bcount;
  35. }
复制代码
看27行,如果有可能,就yield to epoll,线程跑掉,当条件具备,就resume回来继续。
如果逃跑失败,就转成同步阻塞模式。
这个就是协程工作模式。AIO,NIO只有与协程结合才有意义。
作者: yulihua49    时间: 2015-04-08 16:13
本帖最后由 yulihua49 于 2015-04-08 16:28 编辑
yulihua49 发表于 2015-04-08 15:58
看27行,如果有可能,就yield to epoll,线程跑掉,当条件具备,就resume回来继续。
如果逃跑失败,就转成 ...

  1. 5 FIRST:8  04/08 15:00'25 RecvNet:tid=7F50A46DB700,socket=21,yield bcount=0/68
  2. 5 FIRST:8  04/08 15:00'25 thread_work:tid=7F50A46DB700,fiber yield from TCB_no=8
  3. 5 midsc:15198 04/08 15:00'25 thread_work:7F50A46DB700 create fiber for TCB_no=18
  4. 2 midsc:15198 04/08 15:00'25 连接 127.0.0.1,TCB:17,timeout=360
  5. 4 midsc:15198 04/08 15:00'25 do_work: TCB_no=10,tid=7F50A460F700,PROTO_NUM=0 pkg_len=77,t_len=77,O_NODE=16777343,USEC=3637551625043920
  6. 3 FIRST:127.0.0.1:10 04/08 15:00'25 get_s_connect:get pool[0],TCB_no=10,tid:7F50A460F700,USEC=3637551625044062
  7. 5 FIRST:127.0.0.1:10 04/08 15:00'25 get_SC_weight:tid=7F50A460F700,DNODE[1} weight[0]=19
  8. 5 FIRST:127.0.0.1:10 04/08 15:00'25 get_SC_weight:tid=7F50A460F700,DNODE[1} weight[1]=19
  9. 5 FIRST:127.0.0.1:10 04/08 15:00'25 RecvNet:tid=7F50A460F700,socket=23,yield bcount=0/68
  10. 5 FIRST:127.0.0.1:10 04/08 15:00'25 thread_work:tid=7F50A460F700,fiber yield from TCB_no=10
  11. 5 midsc:15198 04/08 15:00'25 thread_work:7F50A460F700 create fiber for TCB_no=19
  12. 2 midsc:15198 04/08 15:00'25 连接 127.0.0.1,TCB:19,timeout=360
  13. 2 midsc:15198 04/08 15:00'25 连接 127.0.0.1,TCB:20,timeout=360
  14. 2 midsc:15198 04/08 15:00'25 连接 127.0.0.1,TCB:18,timeout=360
  15. 4 midsc:15198 04/08 15:00'25 do_work: TCB_no=15,tid=7F50A46DB700,PROTO_NUM=0 pkg_len=77,t_len=77,O_NODE=16777343,USEC=3637551625049720
  16. 3 FIRST:127.0.0.1:15 04/08 15:00'25 get_s_connect:get pool[0],TCB_no=15,tid:7F50A46DB700,USEC=3637551625049876
  17. 5 FIRST:127.0.0.1:15 04/08 15:00'25 get_SC_weight:tid=7F50A46DB700,DNODE[1} weight[0]=18
  18. 5 FIRST:127.0.0.1:15 04/08 15:00'25 get_SC_weight:tid=7F50A46DB700,DNODE[1} weight[1]=19
  19. 2 midsc:15198 04/08 15:00'25 连接 127.0.0.1,TCB:21,timeout=360
  20. 5 FIRST:7  04/08 15:00'25 do_work:call_back TCB_no=7,tid=7F50A460F700,USEC=3637551625050727
  21. 3 FIRST:7  04/08 15:00'25 from_client:TCB:7,tid=7F50A460F700,Send to serv proto_num=0X000A,t_cont=-2147483646,SendPack ret=0,USEC=3637551625050774
  22. 4 midsc:15198 04/08 15:00'25 do_work: TCB_no=13,tid=7F50A460F700,PROTO_NUM=0 pkg_len=77,t_len=77,O_NODE=16777343,USEC=3637551625050810
  23. 3 FIRST:127.0.0.1:13 04/08 15:00'25 get_s_connect:get pool[0],TCB_no=13,tid:7F50A460F700,USEC=3637551625050935
  24. 5 FIRST:127.0.0.1:13 04/08 15:00'25 get_SC_weight:tid=7F50A460F700,DNODE[1} weight[0]=18
  25. 5 FIRST:127.0.0.1:13 04/08 15:00'25 get_SC_weight:tid=7F50A460F700,DNODE[1} weight[1]=18
  26. 5 FIRST:127.0.0.1:13 04/08 15:00'25 RecvNet:tid=7F50A460F700,socket=25,yield bcount=0/68
  27. 5 FIRST:127.0.0.1:13 04/08 15:00'25 thread_work:tid=7F50A460F700,fiber yield from TCB_no=13
  28. 5 midsc:15198 04/08 15:00'25 thread_work:7F50A460F700 create fiber for TCB_no=14
  29. 4 midsc:15198 04/08 15:00'25 do_work: TCB_no=14,tid=7F50A460F700,PROTO_NUM=0 pkg_len=77,t_len=77,O_NODE=16777343,USEC=3637551625052286
  30. 3 FIRST:127.0.0.1:14 04/08 15:00'25 get_s_connect:get pool[0],TCB_no=14,tid:7F50A460F700,USEC=3637551625052427
  31. 5 FIRST:127.0.0.1:14 04/08 15:00'25 get_SC_weight:tid=7F50A460F700,DNODE[1} weight[0]=17
  32. 5 FIRST:127.0.0.1:14 04/08 15:00'25 get_SC_weight:tid=7F50A460F700,DNODE[1} weight[1]=18
  33. 5 FIRST:127.0.0.1:14 04/08 15:00'25 RecvNet:tid=7F50A460F700,socket=26,yield bcount=0/68
  34. 5 FIRST:127.0.0.1:14 04/08 15:00'25 thread_work:tid=7F50A460F700,fiber yield from TCB_no=14
  35. 5 FIRST:11  04/08 15:00'25 thread_work:7F50A460F700 create fiber for TCB_no=11
  36. 4 FIRST:11  04/08 15:00'25 do_work: TCB_no=11,tid=7F50A460F700,PROTO_NUM=0 pkg_len=0,t_len=0,O_NODE=4187505156,USEC=3637551625053288
  37. 3 FIRST:11  04/08 15:00'25 get_s_connect:get pool[0],TCB_no=11,tid:7F50A460F700,USEC=3637551625053304
  38. 5 FIRST:11  04/08 15:00'25 get_SC_weight:tid=7F50A460F700,DNODE[1} weight[0]=17
  39. 5 FIRST:11  04/08 15:00'25 get_SC_weight:tid=7F50A460F700,DNODE[1} weight[1]=17
  40. 5 FIRST:127.0.0.1:15 04/08 15:00'25 RecvNet:tid=7F50A46DB700,socket=24,yield bcount=0/68
  41. 5 FIRST:127.0.0.1:15 04/08 15:00'25 thread_work:tid=7F50A46DB700,fiber yield from TCB_no=15
  42. 5 midsc:15198 04/08 15:00'25 thread_work:7F50A46DB700 create fiber for TCB_no=22
  43. 5 FIRST:11  04/08 15:00'25 RecvNet:tid=7F50A460F700,socket=27,yield bcount=0/68
  44. 5 FIRST:11  04/08 15:00'25 thread_work:tid=7F50A460F700,fiber yield from TCB_no=11
  45. 5 midsc:15198 04/08 15:00'25 thread_work:7F50A460F700 create fiber for TCB_no=16
  46. 4 midsc:15198 04/08 15:00'25 do_work: TCB_no=16,tid=7F50A460F700,PROTO_NUM=0 pkg_len=77,t_len=77,O_NODE=16777343,USEC=3637551625054158
  47. 3 FIRST:127.0.0.1:16 04/08 15:00'25 get_s_connect:get pool[0],TCB_no=16,tid:7F50A460F700,USEC=3637551625054285
  48. 5 FIRST:127.0.0.1:16 04/08 15:00'25 get_SC_weight:tid=7F50A460F700,DNODE[1} weight[0]=16
  49. 5 FIRST:127.0.0.1:16 04/08 15:00'25 get_SC_weight:tid=7F50A460F700,DNODE[1} weight[1]=17
  50. 5 FIRST:127.0.0.1:16 04/08 15:00'25 RecvNet:tid=7F50A460F700,socket=28,yield bcount=0/68
  51. 5 FIRST:127.0.0.1:16 04/08 15:00'25 thread_work:tid=7F50A460F700,fiber yield from TCB_no=16
  52. 5 midsc:15198 04/08 15:00'25 thread_work:7F50A460F700 create fiber for TCB_no=9
  53. 4 midsc:15198 04/08 15:00'25 do_work: TCB_no=9,tid=7F50A460F700,PROTO_NUM=0 pkg_len=77,t_len=77,O_NODE=16777343,USEC=3637551625055294
  54. 3 FIRST:127.0.0.1:9 04/08 15:00'25 get_s_connect:get pool[0],TCB_no=9,tid:7F50A460F700,USEC=3637551625055422
  55. 5 FIRST:127.0.0.1:9 04/08 15:00'25 get_SC_weight:tid=7F50A460F700,DNODE[1} weight[0]=16
  56. 5 FIRST:127.0.0.1:9 04/08 15:00'25 get_SC_weight:tid=7F50A460F700,DNODE[1} weight[1]=16
  57. 5 FIRST:127.0.0.1:9 04/08 15:00'25 RecvNet:tid=7F50A460F700,socket=29,yield bcount=0/68
  58. 5 FIRST:127.0.0.1:9 04/08 15:00'25 thread_work:tid=7F50A460F700,fiber yield from TCB_no=9
  59. 5 FIRST:12  04/08 15:00'25 thread_work:7F50A460F700 create fiber for TCB_no=12
  60. 4 FIRST:12  04/08 15:00'25 do_work: TCB_no=12,tid=7F50A460F700,PROTO_NUM=0 pkg_len=0,t_len=0,O_NODE=2557866597,USEC=3637551625056254
  61. 3 FIRST:12  04/08 15:00'25 get_s_connect:get pool[0],TCB_no=12,tid:7F50A460F700,USEC=3637551625056270
  62. 5 FIRST:12  04/08 15:00'25 get_SC_weight:tid=7F50A460F700,DNODE[1} weight[0]=15
  63. 5 FIRST:12  04/08 15:00'25 get_SC_weight:tid=7F50A460F700,DNODE[1} weight[1]=16
  64. 5 FIRST:12  04/08 15:00'25 RecvNet:tid=7F50A460F700,socket=30,yield bcount=0/68
  65. 5 FIRST:12  04/08 15:00'25 thread_work:tid=7F50A460F700,fiber yield from TCB_no=12
  66. 5 FIRST:8  04/08 15:00'25 thread_work:tid=7F50A460F700,resume to TCB_no=8

复制代码
第二行:tid=7F50A46DB700,fiber yield from TCB_no=8;线程DB700从TCB_no=8 逃出。
下一行它就为TCB_no=18服务了。
66行,TCB_no=8终于resume了,但是线程变为7F50A460F700了。

中间,一群线程抢一堆任务,好乱呀。

作者: vesontio    时间: 2015-04-08 16:16
本帖最后由 vesontio 于 2015-04-08 16:16 编辑

回复 3# yulihua49


你好,这个 do_event 我猜就是所谓的“先去干别的事情吧”,那具体实现的时候是做什么操作呢?
作者: yulihua49    时间: 2015-04-08 16:32
本帖最后由 yulihua49 于 2015-04-08 16:33 编辑
vesontio 发表于 2015-04-08 16:16
回复 3# yulihua49

多线程协程的工作日志。搜一下AIO,ASIO,协程,纤程。。。。。
AIO,NIO如果不与协程结合就没有实际意义。
作者: zhaohongjian000    时间: 2015-04-08 16:40
6楼扯远了吧。

正常逻辑在遇到EAGAIN的时候向epoll注册可写事件,然后去处理其他任务。缓冲区中剩余的数据待可写事件触发后继续写。
作者: windoze    时间: 2015-04-08 17:06
回复 4# yulihua49

你一下给他灌这么多就不怕把他吓跑了?


楼主需要搞清楚一个基本概念,Nonblocking I/O的目的是确保操作流程不会有“意外”的阻塞,有了这个保证,你就可以在同一个流程中安排多组操作而不会有相互影响,这种做法就是“复用”。
所以,Nonblocking I/O天生就是要配合其它东西使用的,单独的一个NIO怎么做都没什么意义。
作者: yulihua49    时间: 2015-04-09 10:04
本帖最后由 yulihua49 于 2015-04-09 20:57 编辑
zhaohongjian000 发表于 2015-04-08 16:40
6楼扯远了吧。

正常逻辑在遇到EAGAIN的时候向epoll注册可写事件,然后去处理其他任务。缓冲区中剩余的数 ...

嗯。你那个是callback形式,IO完成后callback应用逻辑。continuations模型的两种形式:callback和coroutine。callback是由IO函数调用应用,coroutine是应用调用IO函数。
所以改成这样:
AIO,NIO脱离continuations,就没有什么实际意义,与上面哥们是一个意思。
作者: windoze    时间: 2015-04-09 14:11
回复 9# yulihua49

continuation,没有“e”,你拼错了
作者: yulihua49    时间: 2015-04-09 20:58
windoze 发表于 2015-04-09 14:11
回复 9# yulihua49

continuation,没有“e”,你拼错了

多谢,,,改了。。。。。。。
作者: windoze    时间: 2015-04-09 22:05
回复 9# yulihua49

另外,简单的callback不是continuation,call/cc才是
作者: yulihua49    时间: 2015-04-10 13:23
本帖最后由 yulihua49 于 2015-04-10 13:47 编辑
windoze 发表于 2015-04-09 22:05
回复 9# yulihua49

另外,简单的callback不是continuation,call/cc才是

什么是call/cc?call-with-current-continuation

"相当于在程序中打个断点,把这件事挂起。
以后可以一次或多次从断点处开始执行。
可以用来模拟多线程。 ----

这理解太片面了,这只是延续的一个很不常用的用途.
call/cc大部分情况下都是用来从嵌套的过程中跳出的,相当于Common Lisp中的return-from和throw.
只有当把延续保存起来时,才会起到你所说的"断点"效果.
要对延续有较全面的理解最好看看tspl,eopl之类的书.on lisp对延续就是顺便一提,参考价值不大. "

说了半天还是coroutine。

与某侠讨论的结果,callback是continuation的一种形式。或者说是手段。

作者: windoze    时间: 2015-04-10 13:41
回复 13# yulihua49

call/cc不是coroutine,coroutine说的是两个逻辑上无关的routine交错执行。
简单点你可以把call/cc理解为tail function call,逻辑上只有一个routine。

call/cc(不是普通的call)和coroutine本来就是continuation的两种最常见的形式。
作者: BetonArmEE    时间: 2015-04-12 09:35
提示: 作者被禁止或删除 内容自动屏蔽
作者: vesontio    时间: 2015-04-27 15:54
回复 15# BetonArmEE

好的,十分感谢,回头有空看一下!




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2