UNIX网络编程笔记

    针对服务器处理网络连接的几种方式,unix网络编程里给出了8种方案,并且对服务器进程/线程的开销做了一个量化的比较。从个人经验出发,觉得以下几种方式是比较实用的:
    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里,是可以考虑这样实现的。
    unix网络编程里提到,如果有多个进程需要堵塞在同一个socket上,那么堵塞在 accept上比堵塞在select上要好。

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

    6: 在windows上可以采用IOCP的做法(参见文档: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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UNIX环境高级编程笔记是关于在UNIX系统中进行高级编程的一些笔记和技巧的记录。这些笔记主要涉及文件I/O和进程管理等方面的内容。在UNIX系统中,文件I/O是通过文件描述符来进行操作的。文件描述符是一个整数,用来标识打开的文件。为了实现跨平台的兼容性,可以使用POSIX标准来进行文件操作。POSIX是一个操作系统接口的标准,它以UNIX为基础,但并不限于UNIX类系统。此外,Single UNIX Specification简称SUS,它是POSIX.1标准的一个超集,定义了UNIX系统的实现标准。在UNIX系统中,进程的初始化是由init进程来完成的。init进程会读取文件/etc/ttys,并根据其中定义的终端设备进行处理。对于每个允许登录的终端设备,init进程会调用fork函数生成一个子进程,并通过exec函数执行getty程序来处理该终端设备。通过这些技巧和方法,可以实现在UNIX环境下进行高级编程的需求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [UNIX环境高级编程笔记](https://blog.csdn.net/qq_55537010/article/details/127837953)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [《UNIX环境高级编程》学习笔记](https://blog.csdn.net/qq_42526420/article/details/123143423)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值