免费注册 查看新帖 |

Chinaunix

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

关于网络编程(服务端)的一些笔记 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2004-11-11 16:14 |只看该作者 |倒序浏览
共享一点个人笔记,对于高手来说是入门货来的。

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

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

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


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个连接也没有意义了。

论坛徽章:
0
2 [报告]
发表于 2004-11-11 16:51 |只看该作者

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

建议加精!!!!!!!

论坛徽章:
0
3 [报告]
发表于 2004-11-11 20:31 |只看该作者

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

先加入收藏家再说

论坛徽章:
0
4 [报告]
发表于 2004-11-12 10:41 |只看该作者

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

apache2.0.48的代码里
child.c
656行 winnt_get_connection函数调用GetQueuedCompletionStatus
845行 child_main函数里调用CreateIoCompletionPort
很明显用了IOCP

只是poll/epoll方式编程我很少用,虽然看到apache2.0里使用poll代码是不少,但也没看懂是不是EPOLL 。关于EPOLL谁有资料可以指教一下?

论坛徽章:
0
5 [报告]
发表于 2004-11-12 11:03 |只看该作者

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

[quote]原帖由 "flyingbear" 发表:
apache2.0.48的代码里
child.c
656行 winnt_get_connection函数调用GetQueuedCompletionStatus
845行 child_main函数里调用CreateIoCompletionPort
很明显用了IOCP

只是poll/epoll方式编程我很少用,虽

论坛徽章:
0
6 [报告]
发表于 2004-11-12 11:56 |只看该作者

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

了解,和IOCP比较象。apache2.0没有用到这个技术,代码里没有epoll关键字.。继续关注中

论坛徽章:
0
7 [报告]
发表于 2004-11-12 12:40 |只看该作者

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

epoll是个相对较新的属性,下面的帮助页可能对楼主有用。

http://www.xmailserver.org/linux-patches/epoll.txt

论坛徽章:
0
8 [报告]
发表于 2004-11-12 12:55 |只看该作者

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

收藏了。hpunix上不支持这个。不知道能不能安装某些包来支持之。

论坛徽章:
0
9 [报告]
发表于 2004-11-12 13:41 |只看该作者

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

free BSD上的KQUEUE和这个差不多.

论坛徽章:
0
10 [报告]
发表于 2010-07-25 16:06 |只看该作者
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)))
---------------------------------------------------------------------------------------------------------------
不对吧?主动关闭的一方才会出现TIME_WAIT状态的吧?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP