免费注册 查看新帖 |

Chinaunix

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

关于c/s socket中超时问题的总结 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2006-08-25 10:05 |只看该作者 |倒序浏览
本人看了此贴
     http://bbs.chinaunix.net/viewthr ... &extra=page%3D1中思一克斑竹,wyezl 等的讨论,从而有所Think,得出下文,欢迎指正,共同进步!
         
         基于socket tcp的c/s模型中,如果server端只有当client给出request的时候才能作出response的时候
         server端不得不面临这样的设计问题:
         如果client的是异常掉线,比如pc 掉电,拔掉网线等的类似情况,导致server一直占用一些系统资源(
         主要指由于socket引起的)直到大约2小时以后(tcp socket的SO_KEEPALIVE option)server端才会释
         放这些资源。tcp只能保证这些的。 但是,一般不会轻易修改tcp_keepalive_time = 7200(秒)(sysctl)。
         除非为了做一些试验。那么,只能在应用层建立适当的time out机制。
                 
         我总结了如下4种情况,提出了我的处理方案。抛砖引玉。希望大家指正!
         (欢迎拍砖)   
         
         
            
            客户端 可以分为:  A、可控制                        B、不可控制
            服务端 可以分为:  C、select类非多process/thread  D、多process/thread   
           
           所谓客户端可以控制,就是指我们可以修改客户端的源码。
           这里指非process/thread是表示,不是一个client对应一个process/thread
           把select类函数检测的文件描述符集合叫做fdset。
         
           
          A         B
         C  1         3
         D  2         4
         
                           
     一、先说一下第3类:
         客户端为B类型,可能是我们不能修改client的代码,或者我们不愿修改client的代码。
         server端的类型为C的,如果为每一个connection都做一个定时器,不太现实,开销比较大。(尤其是连接数量确实比较大的话)
         
         
         我想到的一个策略是:
         设:
         1.服务端对每个connection,需要做的任务是func(fd);
         2.在服务端维护两个fdset,S(0)和S(1);
         3.在服务端有两个线程work和help且
            a。work线程以周期T(T<2小时)唤醒线程help;(注:T可以用接入server的client个数N替换)
            b。help线程运行的时间为t且t<<T
            
         当周期T到达的时候,work线程的select在检测S(i) i=0或者1;
         这个时候work线程首先唤醒help线程,然后让select检测集合S(1-i),新accpet的fd加入到S(1-i)中;
         对于被唤醒的help线程来说,用select检测S(i),并且在select上设置一个超时时间t;
         
         伪代码如下:
            
         对于worker来说:
         i=0;
         while(true){
                         val=select(S(i));//这个select不设超时
            if (如果是listenfd){
                            fd=accpet(listenfd);
                            S(i)<---fd;
            }else{
           对于每一个可以读的fd:
                    func(fd);           
            }
            if (周期T到达){
               1.从S(i)中去掉listenfd;
                     2.i=1-i;
                     3.listenfd加入到S(i)中;
                     4.唤醒help线程
                     
            }
         }
            
         对于helper线程:         
         while(true){
            rev=select(S(i),t);
            if (rev==超时){
                    1。释放所有资源(清空S(i),如果有必要的话)
                    2。释放cpu,继续等待被worker唤醒
            }else{
                     对于每一个可以读的fd:
                  func(fd);
        //这里需要注意的是对于linux的select,t会被修改为剩余的时间。
         //对于没有修改的api为了精确起见,我们也可以自己写函数修改成类似select的效果。
                                                          
            }
         };
         
         
         我想如果是SMP的系统的话效果会不错。
         
     二、对于第2种类型来说比较简单:
         因为client和server都是可以控制,而且一个client对应一个thread。
         那么client和server可以协商一个协议, 例如client以周期T向server发送一个信号==。
         当然,也可以只在server端用select加一个超时,如果你不需要检测client端的状态的话。
         
         
     三、对于第1种类型来说:
         我个人倾向使用和类型3一样的方法。
         
     四、对于类型4来说:
         目前我只想到只在server端用select加一个超时。

论坛徽章:
0
2 [报告]
发表于 2006-08-25 10:08 |只看该作者
很好的,继续研究和完善该帖子

论坛徽章:
0
3 [报告]
发表于 2006-08-25 17:50 |只看该作者
UNIX网络编程(W.Richard Stevens)的书讲的很清楚.
只有写才能检测到中断.
如果一端能确定某一段时间内必须有数据到达, 可由此判断中断.
楼主的  select 能检测到中断吗?

如果没有收到 FIN, select 不可能检测到什么.

论坛徽章:
0
4 [报告]
发表于 2006-08-25 18:48 |只看该作者
可以考虑用启动独立的探测线程发送OOB心跳~!

论坛徽章:
0
5 [报告]
发表于 2006-08-25 21:30 |只看该作者
原帖由 connet 于 2006-8-25 17:50 发表
如果一端能确定某一段时间内必须有数据到达, 可由此判断中断.
楼主的  select 能检测到中断吗?

在现实中,即便是所谓的“某一段时间内必须有数据到达”,我们也不能简简单单的说:“时间到了,数据没到。现在网络就是断的”。具体的原因可以参考tcp的SO_KEEPALIVE 是怎么判断的。
再说如果有“某一段时间内必须有数据到达”这个条件,某种程度上说你可以掌握(或者说影响)client端的设计,这样的话作为server端的设计者,也就有可能和client之间建立一种协议。就如我文章中说的第2种情况。要注意的是,在第2种情况中,我确实提到了可以使用select,但是那是有条件:“如果你不需要检测client端的状态的话。”

确实,“写”是能够检测状态,但是如果客户端不允许额外的数据怎么办呢?我目前能想到的在tcp的基础上的,只是超时机制。我这片文章主要讨论的是,在server端建立超时机制来尽可能合理的保证server端资源的正确使用!




非常感谢大家的讨论。

[ 本帖最后由 nuclearweapon 于 2006-8-26 09:24 编辑 ]

论坛徽章:
0
6 [报告]
发表于 2006-08-25 21:32 |只看该作者
原帖由 苦中作乐 于 2006-8-25 18:48 发表
可以考虑用启动独立的探测线程发送OOB心跳~!


能不能把您设计的伪代码也写出来,一起完善这片文档呢?
谢谢!

论坛徽章:
0
7 [报告]
发表于 2006-08-25 23:42 |只看该作者
1)服务器对每个连接定时去读或写操作,即使是做个空操作
2)心跳数据非常有用,能解决大部分问题

论坛徽章:
0
8 [报告]
发表于 2006-08-26 00:48 |只看该作者
具体例子UNP上介绍的比较清楚,前一段时间我也在考虑这个事情,基本方法也就是LZ列的几种
(1)使用KEEPALIVE
(2)使用自定义探测数据包保持连接
(3)使用OOB探测
其中最好的我觉得就是OOB技术,因为OOB可以设置不占用普通数据缓冲区,并且OOB信息不会排队,但是有两个问题
(1)OOB是通过信号SIGURG来通知应用进程的,而SIGURG信号是不安全信号,可能丢失,这样可能导致认为丢失信号的连接有问题。
(2)用多线程的话....也有问题
上面是一些简单的认识,有问题的话还忘各位指点~

论坛徽章:
0
9 [报告]
发表于 2006-10-16 17:03 |只看该作者
原帖由 nuclearweapon 于 2006-8-25 10:05 发表
本人看了此贴
     客户端 可以分为:  A、可控制                        B、不可控制
            服务端 可以分为:  C、select类非多process/thread  D、多process/thread  


是不是首先应该是长连接的应用?

我觉得数据流向也很重要:

数据流分为:E: S->C  、F:C->S  、G:S<->C

这个问题确实我也恼火很久了,也是根据不同的应用不同的对待,就是没有通用的方法。

论坛徽章:
39
2017金鸡报晓
日期:2017-02-08 10:39:4219周年集字徽章-周
日期:2023-04-15 12:02:2715-16赛季CBA联赛之深圳
日期:2023-02-16 14:39:0220周年集字徽章-年
日期:2022-08-31 14:25:28黑曼巴
日期:2022-08-17 18:57:0919周年集字徽章-年
日期:2022-04-25 13:02:5920周年集字徽章-20	
日期:2022-03-29 11:10:4620周年集字徽章-年
日期:2022-03-14 22:35:1820周年集字徽章-周	
日期:2022-03-09 12:51:3220周年集字徽章-年
日期:2022-02-10 13:13:4420周年集字徽章-周	
日期:2022-02-03 12:09:4420周年集字徽章-20	
日期:2022-01-25 20:14:27
10 [报告]
发表于 2006-10-16 17:11 |只看该作者
从Multiget摘点代码
连接超時

  1. //with timeout connect
  2. #ifdef WIN32
  3. int CMgSocket::tconnect(SOCKET sock, const struct sockaddr *addr, unsigned int len)
  4. #else
  5. int CMgSocket::tconnect(int sock, const struct sockaddr *addr, unsigned int len)
  6. #endif
  7. {
  8.        
  9.         int ret, val;
  10.         struct timeval tv;
  11.     fd_set sset;

  12. #ifdef WIN32

  13.         int lon;
  14.         u_long iMode=1;
  15.         ioctlsocket(sock,FIONBIO,&iMode);
  16. #else

  17.         int flags,old_flags;

  18.     socklen_t lon;
  19.     flags = old_flags = fcntl(sock, F_GETFL, 0);
  20.         flags |=O_NONBLOCK;
  21.     if (fcntl(sock, F_SETFL, flags) == -1) {
  22.         return -1;
  23.     }               
  24. #endif       
  25.        
  26.         ret= connect(sock, addr, len);

  27.     if(ret < 0)
  28.         {

  29. #ifdef WIN32

  30.         if (WSAEWOULDBLOCK == WSAGetLastError())

  31. #else

  32.         if (errno == EINPROGRESS)

  33. #endif
  34.                 {
  35.             tv.tv_sec = m_nConnectTimeOut;
  36.             tv.tv_usec = 0;
  37.             FD_ZERO(&sset);
  38.             FD_SET(sock, &sset);
  39.             if(select(sock+1, NULL, &sset, NULL, &tv) > 0)
  40.                         {
  41.                 lon = sizeof(int);

  42. #ifdef WIN32
  43.                 getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)(&val), &lon);

  44. #else

  45.                 getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)(&val), &lon);

  46. #endif


  47.                 if(val != 0)
  48.                                 {
  49.                     ret= -1;
  50.                 }
  51.                                 else
  52.                                 {
  53.                                         ret=0;
  54.                                 }
  55.             }
  56.             else
  57.                         {
  58.                 ret= -1;
  59.             }
  60.         }
  61.         else
  62.                 {

  63.             ret = -1;
  64.         }
  65.     }       
  66. #ifdef WIN32
  67.         iMode=0;
  68.         ioctlsocket(sock,FIONBIO,&iMode);
  69. #else       
  70.         fcntl(sock, F_SETFL, old_flags);
  71. #endif
  72.         return ret;

  73. }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP