免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3121 | 回复: 5
打印 上一主题 下一主题

[C] SIO,AIO通用的socket收发接口 [复制链接]

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2015-11-16 14:55 |只看该作者 |倒序浏览
本帖最后由 yulihua49 于 2015-11-16 20:39 编辑

虽然AIO有某些好处,但是程序难写。
你可以用下面的程序进行接口。其中sc.h是我专用的,你换成你的即可,里边就是函数原型。那里边有个:
typedef int (*T_YIELD)(int socket,int rwflg,int timeout);
你的yield功能按这个接口。

要想发送就:SendNet(socket,buf,size,MTU);参数的含义你懂的。
要想接收就: RecvNet(socket,buf,size,timeout);
一般情况下他是同步阻塞的。
如果你能提供协程的yield功能,把yield设置好,他就变成非阻塞的了。
你可以去掉ShowLog或改成你的

  1. cat AIO_tcp.c
  2. /**********************************************
  3. * @(#) SDBC 7.1 TCP RECV/SEND  Tools                      *
  4. * to suport coroutine
  5. **********************************************/
  6. #include <sys/socket.h>
  7. #include <sys/time.h>
  8. #include <unistd.h>
  9. #include <fcntl.h>
  10. #include <sc.h>

  11. #ifndef MIN
  12. #define MIN(a,b) ((a)<(b))?(a):(b)
  13. #endif

  14. static T_YIELD yield=NULL;

  15. T_YIELD get_yield()
  16. {
  17.         return yield;
  18. }

  19. T_YIELD set_yield(T_YIELD new_yield)
  20. {
  21. T_YIELD oldyield=yield;
  22.         yield=new_yield;
  23.         return oldyield;
  24. }

  25. //timeout for second
  26. int RecvNet(int socket,char *buf,int n,int timeout)
  27. {
  28. int bcount=0,br,ret;
  29. int i,repeat=0;
  30. int fflag=-1;

  31.         if(socket<0) return SYSERR;
  32.         if(!buf && n<0) return 0;
  33.         if(yield) {
  34.                 fflag=fcntl(socket,F_GETFL,0);
  35.                 if(fflag!=-1) fcntl(socket,F_SETFL,fflag|O_ASYNC|O_NONBLOCK); //异步操作
  36.         }

  37.         *buf=0;
  38.         br=0;

  39.         while(bcount<n){
  40.                 if((br=read(socket,buf,n-bcount))>0){
  41.                         bcount+=br;
  42.                         buf+=br;
  43.                         repeat=0;
  44.                         continue;
  45.                 }
  46.                 if(fflag==-1 && errno==EAGAIN) return TIMEOUTERR;
  47.                 if(br<=0 && errno && errno != EAGAIN){
  48.                     if(errno!=ECONNRESET)
  49.                         ShowLog(1,"%s:br=%d,err=%d,%s",__FUNCTION__,br,errno,strerror(errno));
  50.                     break;
  51.                 }
  52.                 if(bcount < n && fflag!=-1) { //切换任务
  53.                         if(repeat++>3) return -errno;
  54. ShowLog(5,"%s:tid=%lX,socket=%d,yield to schedle bcount=%d/%d",__FUNCTION__,pthread_self(),socket,bcount,n);
  55.                         i=yield(socket,0,timeout);
  56.                         if(i<0) {
  57.                           if(timeout>0) {
  58.                                 struct timeval tmout;
  59.                                 tmout.tv_sec=timeout;
  60.                                 tmout.tv_usec=0;
  61.                                 ret=setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,(char *)&tmout,sizeof(tmout));
  62.                           }
  63.                                 fcntl(socket,F_SETFL,fflag);
  64.                                 fflag=-1;
  65.                                 if(i==-11) return -11;
  66.                         }
  67.                 }
  68.         }
  69.         if(fflag!=-1) fcntl(socket,F_SETFL,fflag);
  70.         return bcount==0?-1:bcount;
  71. }

  72. int SendNet(int socket,char *buf,int n,int MTU)
  73. {
  74. int bcount,bw;
  75. int sz,i=0;
  76. int fflag=-1;
  77. size_t SendSize;

  78.         if(socket<0) return SYSERR;
  79.         if(yield) {
  80.                 fflag=fcntl(socket,F_GETFL,0);
  81.                 if(fflag != -1) fcntl(socket,F_SETFL,fflag|O_NONBLOCK); //异步操作
  82.         }
  83.         bcount=0;
  84.         bw=0;
  85.         if(MTU>500) SendSize=MTU;
  86.         else SendSize=n;
  87.         while(bcount<n){
  88.                 sz=MIN(n-bcount,SendSize);
  89.                 if((bw=write(socket,buf,sz))>0){
  90.                         bcount+=bw;
  91.                         buf+=bw;
  92.                 }
  93.                 if(bw<0&&errno!=EAGAIN) {
  94.                         ShowLog(1,"%s:err=%d,%s",__FUNCTION__,errno,strerror(errno));
  95.                         break;
  96.                 }
  97.                 if(bw < sz && fflag != -1) { //切换任务
  98. ShowLog(5,"%s:tid=%lX,socket=%d,yield bw=%d/%d",__FUNCTION__,pthread_self(),socket,bw,sz);
  99.                     i=yield(socket,1,0);
  100.                     if(i<0) {
  101.                         fcntl(socket,F_SETFL,fflag);
  102.                         fflag = -1;
  103.                     }
  104.                 }
  105.         }
  106.         if(fflag != -1)  fcntl(socket,F_SETFL,fflag);
  107.         return bcount==0?-1:bcount;
  108. }

复制代码

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
2 [报告]
发表于 2015-11-16 18:02 |只看该作者
这个没有说清回调函数是在哪个context下跑呢?

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
3 [报告]
发表于 2015-11-16 20:41 |只看该作者
本帖最后由 yulihua49 于 2015-11-16 21:08 编辑
windoze 发表于 2015-11-16 18:02
这个没有说清回调函数是在哪个context下跑呢?

它是通用的,不涉及特定的context。
context是在你的yield里,自己定义和使用。

我是在yield里,通过线程池记录的thread context和user context交换。
以当前线程号为索引,在线程池里查找。如果是foriegn thread就返回-5,
超时返回-11.正常resume返回0;
ret!=0 就改阻塞方式了。这个模块经过调试。是我现在用的。

明天发个yield程序。供参考,你们可以使用你们的yield,按这个接口即可。
对于初友,先用这两个程序完成普通的网络IO,实现主要功能,今后长进了,加入coroutine,自动成了NIO。其他代码不用大改。
对于我来说,我的中间件要支持PPC、TPC、TPOOL模式,前二种不需要NIO,只有3需要NIO和协程。三种模式可以共用底层模块。
在3下,set_yield,就成了NIO。
玩coroutine很久才想出这个全通用,全透明的方案。

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
4 [报告]
发表于 2015-11-17 14:08 |只看该作者
本帖最后由 yulihua49 于 2015-11-18 15:46 编辑
yulihua49 发表于 2015-11-16 20:41
它是通用的,不涉及特定的context。
context是在你的yield里,自己定义和使用。

在线程池服务模块里:

  1. static T_YIELD other_yield=NULL;//保存先前应用设置的yield。

  2. TPOOL_srv()
  3. {
  4. ........
  5.         other_yield=set_yield(do_yield);//返回通常是NULL。
  6. .......
  7.         服务循环
  8. ......
  9. }

复制代码
那么,do_yield是什么呢?

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
5 [报告]
发表于 2015-11-17 14:15 |只看该作者
本帖最后由 yulihua49 于 2015-11-18 15:47 编辑
yulihua49 发表于 2015-11-17 14:08
在线程池服务模块里:那么,do_yield是什么呢?

  1. static int do_yield(int socket,int rwflg,int timeout)
  2. {
  3. int ret=do_event(socket,rwflg,timeout);
  4.         if(THREAD_ESCAPE == ret && other_yield)
  5.                 ret=other_yield(socket,rwflg,timeout);
  6.         return ret;
  7. }
复制代码
可以看到,这个方案支持多个线程组分别进行yield,每个都可以不同的context和yield方法。

首先在服务器线程组进行,如果是foriegn thread,就转入应用提供的线程组,再不行就转入失败,进入同步模式。

那么,do_event是什么呢?

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
6 [报告]
发表于 2015-11-17 14:21 |只看该作者
本帖最后由 yulihua49 于 2015-11-17 14:27 编辑
yulihua49 发表于 2015-11-17 14:15
那么,do_event是什么呢? ...

它就是先在本服务器线程组里查找当前线程,然后。。。。

  1. //yield to schedle
  2. static int do_event(int sock,int flg,int timeout)
  3. {
  4. TCB *task;
  5. int save_fd,save_timeo=-1;
  6. int ret;
  7. pthread_t tid=pthread_self();
  8. resource *rs=tpool.pool;

  9.         for(ret=0;ret<tpool.num;ret++,rs++) { //找原来的线程
  10.                 if(tid == rs->tid) break;
  11.         }
  12.         if(ret>=tpool.num) return THREAD_ESCAPE;
  13.         task=(TCB *)rs->tc.uc_link;//在这里拿到了context
  14.         if(!task || task->uc.uc_stack.ss_size == 0) return MEMERR;
  15.         save_fd=task->fd;
  16.         if(timeout>0) {
  17.                 save_timeo=task->timeout;
  18.                 task->timeout=timeout;
  19.         }
  20.         task->AIO_flg=flg+1;
  21.         task->fd=sock;
  22.         task->timestamp=now_usec();
  23.         pthread_mutex_lock(&task->lock);//防止其他线程提前闯入
  24.         ret=do_epoll(task,0,flg);
  25.         if(ret<0) {
  26.                 pthread_mutex_unlock(&task->lock);
  27.                 task->fd=save_fd;
  28.                 task->AIO_flg=0;
  29.                 if(save_timeo>-1) task->timeout=save_timeo;
  30.                 return FORMATERR;
  31.         }
  32.         ret=swapcontext(&task->uc,&rs->tc);//这才是真正的yield
  33.         pthread_mutex_unlock(&task->lock);
  34.         task->fd=save_fd;
  35.         task->AIO_flg=0;
  36.         if(save_timeo>-1) task->timeout=save_timeo;
  37.         if(flg==0 && task->events != EPOLLIN) {
  38.                 ShowLog(1,"%s:tid=%lX resumed events=%X",__FUNCTION__,
  39.                         pthread_self(),task->events);
  40.                 return -11;
  41.         }
  42.         return ret;
  43. }
复制代码
好了,所有的秘密都在这里了。你可以写自己的yield了吗?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP