分享
 
 
 

关于网络编程(服务端)的一些笔记

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

关于网络编程(服务端)的一些笔记

http://vcbear.mblogger.cn

针对服务器处理网络连接的几种方式,unix网络编程里给出了9种方案,并且对服务器进程/线程的开销做了一个量化的比较。从个人经验出发,觉得以下几种方式是比较实用的:

1.最简单的是堵塞Accept,收到连接后fork进程(unix)或创建Thread.原进程/线程继续堵塞Accept,创出来的进程线程只处理新连接上的客户请求。如果忽略创建进程/线程的开销,以及每个连接必须对应一个进程/线程的话,做成这样已经可以满足绝大部分简单的应用服务器需要了。qmail的tcpserver用的也是这种模式。要注意的是,如果子进程不监听连接,最好关掉继承自父进程的原来listen的socket.由于fork的开销比较大,如果对服务器的响应非常苛求,这种方式还是不实用的。另外,fork一个单线程的进程,会比fork一个多线程的进程要快的多。

2.引入进程/线程池概念,也就是先创建一堆进程/线程,让这些进程/线程来处理连接事件和客户端业务。在多线程支持的系统里,实现线程池还是很容易的,只让一个线程在accept上堵塞,收到连接后,唤醒一个本来堵塞在某个互斥量上的线程来处理之或将其放入队列等待处理。这样处理客户端连接的线程可以无限多(系统容量范围内)。但在进程模式里,因为除非使用fork让子进程继承连接句柄,否则跨进程处理accept返回的连接。(某些unix可以实现进程间传递句柄,win32也有进程复制句柄的做法,但这跟操作系统结合太紧,而且不是所有操作系统都支持,忽略之)

vcbear.mblogger.cn

3.解决上述进程问题就是让所有进程都能监听同一个端口并accept连接,可以通过一个进程创建了listen socket,并fork同样进行listen/accept的子进程来实现。这里要考虑惊群效应,也就是说有连接到来,所有堵塞在监听socket上的进程可能都会被系统唤醒,但只有一个进程能够从tcp协议栈里获得连接。惊群效应引入了进程调度开销,解决办法是在accept上加锁。同时只有一个进程在调用accept.在获得连接后,该进程不再调用Accept,而是处理客户端业务,直到客户端退出,才重新回归调用accept。Apache1.3.x使用的是类似的模式,可以从其http_main.c里看到。

典型代码如下

while(true)

{

mutex_lock

newcliet=accept(listener)

mutex_unlock

while(true)

{

/*处理客户端任务直到客户端断开*/

}

}

4.以上都只考虑了一个进程/线程对应一个连接的情况,当有大量的连接时,就可能会产生大量的线程。使用select可以让一个线程/进程处理多个连接。如下代码

if( select( .... ) ) > 0 )/*>0表示select集合里有事件发生*/

{

/*依次检查各有效的socket*/

for()

{

if(FD_ISSET( sock... )/**/

{

if(sock == ServerSocket){ /*是监听socket,调用accept,得到新socket,并加到本进程的select集合里*/ }

else{/*其他socket*/}

}

}

}

结合3的模式,比如创建10个都能监听的进程,每个进程最多处理10个客户端连接,那么进程/连接数比就降低了10倍。但这种情况下就不能对监听socket加锁,无法避免惊群问题。可以看到,在select后的for,同样可能占用不少cpu,比起系统的进程调度可能是有过之而无不及。在必须用单进程处理多个连接的case里,是可以考虑这样实现的。PS:unix网络编程里提到,如果有多个进程需要堵塞在同一个socket上,那么堵塞在accept上比堵塞在select上要好。

5:说到select,在合适的地方使用select还是不错的,尤其是读socket,使用select可以有效的实现可超时的堵塞方式,而不是永久性堵塞。在网络编程里冒把进程/线程堵塞致死的风险是很不应该的。所以最好把socket设置成非堵塞的,这样读函数可以立刻返回,读到数据或产生错误,错误码EAGAIN/EINTR/EINVAL表示连接应该没有断开,可以继续使用。

6:在windows上可以采用IOCP的做法(参见N年前翻译的文档:http://blog.csdn.net/vcbear/archive/2001/08/29/5987.aspx)。用单进程处理多个连接,让操作系统去操心网络上的事件,并挑出来是哪个连接上产生了IO。这样把任务调度的细节放到了操作系统内核,避免了在应用层上的进程/线程开销。据说LINUX/UNIX上有类似的EPOLL,怀疑Apache2.0采用了这个技术,还没有来得及研究。但如果通用代码方案已经可以满足要求,我觉得应该尽量避免使用和操作系统极度相关的代码,比如WIN32的IOCP。

7:考虑服务器上的进程调度,进程限制,信息共享是比较精细的事情。一般的实现就是做个共享内存公告板,进程竞争的读写公告板上的信息,报告自己的状态或获取其他信息。apache在这一块是做的很漂亮的,它可以根据连接的忙闲程度,由一个主进程来创建更多处理进程或控制空闲进程退出。过多的进程/线程存在还是会影响系统效率的(量化计算参考unix网络编程卷一的第27章)

8:一点注意:服务器编程一定要设置linger,否则客户端主动socket关闭的时候,服务器会持续2*TIME_WAIT的时间才真正断开,造成大量的废连接负担。

rLinger.l_onoff = 1; // 打开linegr开关

rLinger.l_linger = 0; // 设置延迟时间为 0 秒, 注意 TCPIP立即关闭,但是有可能出现化身

setsockopt(nSockfd, SOL_SOCKET, SO_LINGER, (char *)&rLinger, sizeof(rLinger)))

另外一个选项SO_REUSEADDR也是server socket必须设置的。

无论采用什么模式来编写服务器,需要关注的问题除了网络事件响应,进程/线程调度之外,还有服务器本身的容量问题。最短的木板其实并不在于处理网络连接的模型本身,而是服务器上的数据处理能力或网络带宽,如果一台服务器及其网络带宽受理100个客户端的业务就已经很吃力了(比如ftp,mud,p2p,其他应用服务...),那么其socket服务器就算能轻松处理10000个连接也没有意义了。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有