• 提高网络效率的总结


    1. 异步化,

    以epoll为代表。libevent也是基于epoll而实现的。

    2. 消息驱动,

    是跟异步化相结合,reactor模式。另有Scala的Actor模式,是完全的消息交互。

    3. 一些TCP参数,

    比如禁掉Nagle选项,不要打开CORK算法,使得发包和ack不要延迟太多。

    4. SO_LINGER

    解决TIME_WAIT状态过多的问题。尤其是短连接。可以参考 http://www.cnblogs.com/jdonson/p/4760110.html

    如下:

    不管长连接还是短连接,连接建立->数据传输->连接关闭的流程和处理都是一样的。

    正常的TCP客户端连接在关闭后,会进入一个TIME_WAIT的状态,持续的时间一般在1~4分钟,对于连接数不高的场景,1~4分钟其实并不长,对系统也不会有什么影响,
    但如果短时间内(例如1s内)进行大量的短连接,则可能出现这样一种情况:客户端所在的操作系统的socket端口和句柄被用尽,系统无法再发起新的连接!

    举例来说:假设每秒建立了1000个短连接(Web场景下是很常见的,例如每个请求都去访问memcached),假设TIME_WAIT的时间是1分钟,则1分钟内需要建立6W个短连接,
    由于TIME_WAIT时间是1分钟,这些短连接1分钟内都处于TIME_WAIT状态,都不会释放,而Linux默认的本地端口范围配置是:
    net.ipv4.ip_local_port_range
    = 32768 61000 不到3W,因此这种情况下新的请求由于没有本地端口就不能建立了

    可以通过如下方式来解决这个问题:
    1)可以改为长连接,但代价较大,长连接太多会导致服务器性能问题,而且PHP等脚本语言,需要通过proxy之类的软件才能实现长连接
    2)修改ipv4.ip_local_port_range,增大可用端口范围,但只能缓解问题,不能根本解决问题;
    3)客户端程序中设置socket的SO_LINGER选项
    4)客户端机器打开tcp_tw_recycle和tcp_timestamps选项;
    5)客户端机器打开tcp_tw_reuse和tcp_timestamps选项;
    6)客户端机器设置tcp_max_tw_buckets为一个很小的值;

    SO_LINGER的学习见http://blog.csdn.net/tengyft/article/details/45827193

    SO_LINGER选项用于控制close系统调用在关闭TCP连接时的行为。默认情况下,当我们使用close系统调用来关闭一个socket时,close将立即返回,TCP模块负责把该socket对应的TCP发送缓冲区中残留的数据发送给对方。

    #include <sys/socket.h>
    struct linger
    {
        int l_onoff;// 开启(非0)还是关闭(0)该选项
        int l_linger;// 滞留时间
    };

    根据linger结构体中两个成员变量的不同值,close系统调用可能产生如下3种行为之一: 

    (1)l_onoff等于0(关闭)。此时SO_LINGER选项不起作用,close用默认行为来关闭socket。 

    (2)l_onoff不为0(开启),l_linger等于0。此时close系统调用立即返回,TCP模块将丢弃被关闭的socket对应的TCP发送缓冲区中残留的数据,同时给对方发送一个复位报文段(RST)。因此,这种情况给服务器提供了异常终止一个连接的方法。 

    (3)l_onoff不为0(开启),l_linger大于0。此时close的行为取决于两个条件:一是被关闭的socket对应的TCP发送缓冲区是否还有残留的数据;二是该socket是阻塞的,还是非阻塞的。对于阻塞的socket,close将等待一段长为l_linger的时间,直到TCP模块发送完所有残留数据并得到对方的确认。如果这段时间内TCP模块没有发送完残留数据并得到对方的确认,那么close系统调用将返回-1并设置errno为EWOULDBLOCK。如果socket是非阻塞的,close将立即返回,此时我们需要根据其返回值和errno来判断残留数据是否已经发送完毕。

    注:Linux下 EWOULDBLOCK貌似就是EAGAIN

    除了上面这两个errno,要注意的是 EINTR指操作被中断唤醒,需要重新读/写

    EAGAIN不是一种错误。在VxWorks和Windows上,EAGAIN的名字叫做EWOULDBLOCK。

    另外,如果出现EINTR即errno为4,错误描述Interrupted system call,操作也应该继续

    最后,如果recv的返回值为0那表明连接已经断开,我们的接收操作也应该结束。

    5. SO_REUSEADDR

    另外还有 SO_REUSEADDR选项:

    SO_REUSEADDR可以用在以下四种情况下。

    (摘自《Unix网络编程》卷一,即UNPv1)

    1、当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。

    2、SO_REUSEADDR允许同一port上启动同一服务器的多个实例(多个进程)。但每个实例绑定的IP地址是不能相同的。在有多块网卡或用IP Alias技术的机器可以测试这种情况。

    3、SO_REUSEADDR允许单个进程绑定相同的端口到多个socket上,但每个socket绑定的ip地址不同。这和2很相似,区别请看UNPv1。

    4、SO_REUSEADDR允许完全相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP。

     

    还有一个:SO_REUSEPORT选项有如下语义:

        此选项允许完全重复捆绑,但仅在想捆绑相同IP地址和端口的套接口都指定了此套接口选项才行。

        如果被捆绑的IP地址是一个多播地址,则SO_REUSEADDR和SO_REUSEPORT等效。

    使用这两个套接口选项的建议:

        在所有TCP服务器中,在调用bind之前设置SO_REUSEADDR套接口选项;

     

    用法如下:

    int option = 1;
    if (setsockopt ( masterSocket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option) ) < 0)
    {
       die( "setsockopt" );
    }

    Q: 编写 TCP/SOCK_STREAM 服务程序时,SO_REUSEADDR到底什么意思?

    A: 这个套接字选项通知内核,如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息,指明"地址已经使用中"。如果你的服务程序停止后想立即重启,而新套接字依旧使用同一端口,此时 SO_REUSEADDR 选项非常有用。必须意识到,此时任何非期望数据到达,都可能导致服务程序反应混乱,不过这只是一种可能,事实上很不可能。

    一个套接字由相关五元组构成,协议、本地地址、本地端口、远程地址、远程端口。SO_REUSEADDR 仅仅表示可以重用本地本地地址、本地端口,整个相关五元组还是唯一确定的。所以,重启后的服务程序有可能收到非期望数据。必须慎重使用 SO_REUSEADDR 选项。

     

     

  • 相关阅读:
    形态学操作
    形态学腐蚀膨胀操作
    图像模糊操作
    OpenCV像素操作和图形绘制
    c++中char类型的取值范围
    二叉树基本操作
    剑指27 二叉树的镜像
    剑指26 树的子结构
    剑指24: 反转链表
    剑指22 链表倒数第k个节点
  • 原文地址:https://www.cnblogs.com/charlesblc/p/6260416.html
Copyright © 2020-2023  润新知