本人看了此贴
http://bbs.chinaunix.net/viewthread.php?tid=813588&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加一个超时。
思一克 回复于:2006-08-25 10:08:36
很好的,继续研究和完善该帖子
connet 回复于:2006-08-25 17:50:42
UNIX网络编程(W.Richard Stevens)的书讲的很清楚.
只有写才能检测到中断.
如果一端能确定某一段时间内必须有数据到达, 可由此判断中断.
楼主的 select 能检测到中断吗?
如果没有收到 FIN, select 不可能检测到什么.
苦中作乐 回复于:2006-08-25 18:48:56
可以考虑用启动独立的探测线程发送OOB心跳~!
nuclearweapon 回复于:2006-08-25 21:30:50
引用:原帖由 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 编辑 ]
nuclearweapon 回复于:2006-08-25 21:32:09
引用:原帖由 苦中作乐 于 2006-8-25 18:48 发表
可以考虑用启动独立的探测线程发送OOB心跳~!
能不能把您设计的伪代码也写出来,一起完善这片文档呢?
谢谢!
benlan 回复于:2006-08-25 23:42:05
1)服务器对每个连接定时去读或写操作,即使是做个空操作
2)心跳数据非常有用,能解决大部分问题
苦中作乐 回复于:2006-08-26 00:48:28
具体例子UNP上介绍的比较清楚,前一段时间我也在考虑这个事情,基本方法也就是LZ列的几种
(1)使用KEEPALIVE
(2)使用自定义探测数据包保持连接
(3)使用OOB探测
其中最好的我觉得就是OOB技术,因为OOB可以设置不占用普通数据缓冲区,并且OOB信息不会排队,但是有两个问题
(1)OOB是通过信号SIGURG来通知应用进程的,而SIGURG信号是不安全信号,可能丢失,这样可能导致认为丢失信号的连接有问题。
(2)用多线程的话....也有问题
上面是一些简单的认识,有问题的话还忘各位指点~
yuanyawei 回复于:2006-10-16 17:03:54
引用:原帖由 nuclearweapon 于 2006-8-25 10:05 发表
本人看了此贴
客户端 可以分为: A、可控制 B、不可控制
服务端 可以分为: C、select类非多process/thread D、多process/thread
是不是首先应该是长连接的应用?
我觉得数据流向也很重要:
数据流分为:E: S->C 、F:C->S 、G:S<->C
这个问题确实我也恼火很久了,也是根据不同的应用不同的对待,就是没有通用的方法。
醉卧水云间 回复于:2006-10-16 17:11:13
从Multiget摘点代码
连接超時
//with timeout connect
#ifdef WIN32
int CMgSocket::tconnect(SOCKET sock, const struct sockaddr *addr, unsigned int len)
#else
int CMgSocket::tconnect(int sock, const struct sockaddr *addr, unsigned int len)
#endif
{
int ret, val;
struct timeval tv;
fd_set sset;
#ifdef WIN32
int lon;
u_long iMode=1;
ioctlsocket(sock,FIONBIO,&iMode);
#else
int flags,old_flags;
socklen_t lon;
flags = old_flags = fcntl(sock, F_GETFL, 0);
flags |=O_NONBLOCK;
if (fcntl(sock, F_SETFL, flags) == -1) {
return -1;
}
#endif
ret= connect(sock, addr, len);
if(ret < 0)
{
#ifdef WIN32
if (WSAEWOULDBLOCK == WSAGetLastError())
#else
if (errno == EINPROGRESS)
#endif
{
tv.tv_sec = m_nConnectTimeOut;
tv.tv_usec = 0;
FD_ZERO(&sset);
FD_SET(sock, &sset);
if(select(sock+1, NULL, &sset, NULL, &tv) > 0)
{
lon = sizeof(int);
#ifdef WIN32
getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)(&val), &lon);
#else
getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)(&val), &lon);
#endif
if(val != 0)
{
ret= -1;
}
else
{
ret=0;
}
}
else
{
ret= -1;
}
}
else
{
ret = -1;
}
}
#ifdef WIN32
iMode=0;
ioctlsocket(sock,FIONBIO,&iMode);
#else
fcntl(sock, F_SETFL, old_flags);
#endif
return ret;
}
醉卧水云间 回复于:2006-10-16 17:14:38
读写超時:
//set timeout here
//set the timeout value to socket
#ifdef WIN32
setsockopt( m_Sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&m_nTimeOut, sizeof( int ) );
setsockopt( m_Sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&m_nTimeOut, sizeof( int ) );
#else
struct timeval tv;
tv.tv_sec=m_nTimeOut;
tv.tv_usec=0;
//A returned value of 0 indicates the system will not time out. The maximum value is 32 767 seconds.
//Return code 0 indicates that the function was successful.A return code equal to -1 indicates an error
setsockopt( m_Sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof( tv ) );
setsockopt( m_Sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof( tv ) );
#endif
yuanyawei 回复于:2006-10-16 17:27:56
setsockopt( m_Sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&m_nTimeOut, sizeof( int ) );
如果到时间了确实没数据来怎么办?认为是连接断开了吗?应该不可以,那怎么办?
你试试拔网线看看?
其实重点也不在客户端,在于服务端如何处理。
醉卧水云间 回复于:2006-10-16 20:14:55
引用:原帖由 yuanyawei 于 2006-10-16 17:27 发表
setsockopt( m_Sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&m_nTimeOut, sizeof( int ) );
如果到时间了确实没数据来怎么办?认为是连接断开了吗?应该不可以,那怎么办?
你试试拔网线看看?
其实重点也 ...
超時会错,你可以认为连接无效了。
客户端和服务端连接建立后没什么区别。
langue 回复于:2006-10-16 20:21:12
引用:原帖由 醉卧水云间 于 2006-10-16 20:14 发表
超時会错,你可以认为连接无效了。
客户端和服务端连接建立后没什么区别。
同意第二句话。服务器和客户端,角色有时候可以互换。木马控制端就是实际的客户。
醉卧水云间 回复于:2006-10-16 21:01:08
引用:原帖由 langue 于 2006-10-16 20:21 发表
同意第二句话。服务器和客户端,角色有时候可以互换。木马控制端就是实际的客户。
第一句话呢:lol:,超時会有超時错,应该正确吧。
昨天有个美国华人说要帮我把MultiGet移植到Mac上,不过忘记问他那个介词应该用in还是用under了,下回问问再告诉你他的看法,我个人还很迷惑于这个问题。
yuanyawei 回复于:2006-10-17 10:01:07
引用:原帖由 nuclearweapon 于 2006-8-25 10:05 发表
基于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机制。
愿意是讨论服务端如何判断客户端拔了网线后的处理,在负荷大的服务端,这样会浪费很多服务端资源,而且这种情况在有些业务上是需要控制的,而不是简单的判断超时的作法。
|