zoukankan      html  css  js  c++  java
  • linux下connect超时时间探究

    最近在linux做服务器开发的时候,发现了一个现象:服务器在启动的时候调用了 connect 函数,因为连接了一个不可用的端口,导致connect最后报出了 “Connection timed out” 的错误。但是这中间过了六十多秒的时间。

    为何会等待这么长的时间才超时呢?这个时间又在哪里设置?

    《UNIX网络编程(第一卷)——套接口 API 和 X/Open 传输接口 API》一书的4.3节有写到:

      对于TCP套接口来说,函数 connect 激发TCP的三路握手过程,且仅在链接成功建立或出错时才返回,返回的错误可能有如下几种情况:

      1. 如果TCP客户没有收到SYN分节的响应,则返回ETIMEDOUT。例如在4.4BSD中,当调用函数 connect 时,发出一个SYN,若无响应,等待6秒之后再发一个;若仍无响应,24秒钟之后再发一个。若总共等待了75秒钟之后仍未响应,则返回错误...

    从书中可以看到 connect 建立TCP链接的过程中,会发送SYN包,如果没有收到SYN包的回包,内核会多次发送SYN包,并且每次重试的间隔会逐渐增加,避免发送太多的SYN包影响网络。

    在CentOS上,这个重试次数是可以设置的:

    $ sysctl net.ipv4 | grep tcp
    net.ipv4.tcp_timestamps = 1
    net.ipv4.tcp_window_scaling = 1
    net.ipv4.tcp_sack = 1
    net.ipv4.tcp_retrans_collapse = 1
    net.ipv4.tcp_syn_retries = 5
    net.ipv4.tcp_synack_retries = 5
    net.ipv4.tcp_max_orphans = 262144
    net.ipv4.tcp_max_tw_buckets = 262144
    net.ipv4.tcp_keepalive_time = 7200
    net.ipv4.tcp_keepalive_probes = 9
    net.ipv4.tcp_keepalive_intvl = 75
    net.ipv4.tcp_retries1 = 3
    net.ipv4.tcp_retries2 = 15
    net.ipv4.tcp_fin_timeout = 60
    net.ipv4.tcp_syncookies = 1
    net.ipv4.tcp_tw_recycle = 0
    net.ipv4.tcp_abort_on_overflow = 0
    net.ipv4.tcp_stdurg = 0
    net.ipv4.tcp_rfc1337 = 0
    net.ipv4.tcp_max_syn_backlog = 2048
    net.ipv4.tcp_orphan_retries = 0
    net.ipv4.tcp_fack = 1
    net.ipv4.tcp_reordering = 3
    net.ipv4.tcp_ecn = 2
    net.ipv4.tcp_dsack = 1
    net.ipv4.tcp_mem = 639168       852224  1278336
    net.ipv4.tcp_wmem = 4096        16384   4194304
    net.ipv4.tcp_rmem = 4096        87380   4194304
    net.ipv4.tcp_app_win = 31
    net.ipv4.tcp_adv_win_scale = 2
    net.ipv4.tcp_tw_reuse = 0
    net.ipv4.tcp_frto = 2
    net.ipv4.tcp_frto_response = 0
    net.ipv4.tcp_low_latency = 0
    net.ipv4.tcp_no_metrics_save = 0
    net.ipv4.tcp_moderate_rcvbuf = 1
    net.ipv4.tcp_tso_win_divisor = 3
    net.ipv4.tcp_congestion_control = cubic
    net.ipv4.tcp_abc = 0
    net.ipv4.tcp_mtu_probing = 0
    net.ipv4.tcp_base_mss = 512
    net.ipv4.tcp_workaround_signed_windows = 0
    net.ipv4.tcp_challenge_ack_limit = 1000
    net.ipv4.tcp_limit_output_bytes = 262144
    net.ipv4.tcp_dma_copybreak = 4096
    net.ipv4.tcp_slow_start_after_idle = 1
    net.ipv4.tcp_available_congestion_control = cubic reno
    net.ipv4.tcp_allowed_congestion_control = cubic reno
    net.ipv4.tcp_max_ssthresh = 0
    net.ipv4.tcp_thin_linear_timeouts = 0
    net.ipv4.tcp_thin_dupack = 0
    net.ipv4.tcp_min_tso_segs = 2
    net.ipv4.tcp_invalid_ratelimit = 500

    其中的 net.ipv4.tcp_syn_retries 选项控制着SYN的重试次数,可以通过如下命令来查看和设置:

    $ sysctl net.ipv4.tcp_syn_retries       #查看
    net.ipv4.tcp_syn_retries = 5
    $ sudo sysctl -w net.ipv4.tcp_syn_retries=1 #设置
    net.ipv4.tcp_syn_retries = 1

    下面用一个简单的程序,来验证各种次数下的connect超时时间:

    #include <iostream>
    #include <sys/socket.h>
    #include <sys/time.h>
    #include <netinet/in.h>
    #include <errno.h>
    #include <string.h>
    #include <arpa/inet.h>
    
    long long GetCurrentMSecond()
    {
        struct timeval tv; 
        gettimeofday(&tv, NULL);
        return tv.tv_sec * 1000 + tv.tv_usec / 1000;
    }
    
    int main()
    {
        int fd = 0;
        struct sockaddr_in addr;
    
        fd = socket(AF_INET, SOCK_STREAM, 0); 
    
        socklen_t bufSize = 128 * 1024;
        int retCode = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufSize, sizeof(bufSize));
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr("192.168.207.128");
        addr.sin_port = htons(13500); //连接一个不用的端口,以保证会触发超时
        long long llBeginTime = GetCurrentMSecond();
        if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) 
        {   
            long long llEndTime = GetCurrentMSecond();
            std::cout << "connect failed, errno: " << errno << ", error: " << strerror(errno)
                      << ", cost time: " << llEndTime - llBeginTime << std::endl;
            return 0;
        }   
        std::cout << "connect success" << std::endl;
    }

    通过设置不同的重试次数,探究各种重试次数下的time out时间:

    $ g++ connect.cpp -o main
    $ sudo sysctl -w net.ipv4.tcp_syn_retries=1
    net.ipv4.tcp_syn_retries = 1
    $ ./main 
    connect failed, errno: 110, error: Connection timed out, cost time: 3000
    $ sudo sysctl -w net.ipv4.tcp_syn_retries=2
    net.ipv4.tcp_syn_retries = 2
    $ ./main 
    connect failed, errno: 110, error: Connection timed out, cost time: 7000
    
    重试次数 超时时间(单位:毫秒)
    1 3000  
    2 7000
    3 14999
    4 31000
    5 63001
    6 126999

    从表格中可以看到,当前设置重试次数为5的时候,超时时间是63秒,可以通过修改重试次数的方式,来改变connect的超时时间。

  • 相关阅读:
    从零开始学习jQuery (一) 开天辟地入门篇
    c#获取当前应用程序所在路径
    软件和需求的实践
    [zt]如何将数据导入到 SQL Server Compact Edition 数据库中(四)
    C++与C#内存管理对比分析 (转载)
    [zt]如何将数据导入到 SQL Server Compact Edition 数据库中(五)
    [zt]如何将数据导入到 SQL Server Compact Edition 数据库中(三)
    SharePoint服务器修改域和机器名
    <处世36计>之第35计谈判计
    软件工程之需求分析
  • 原文地址:https://www.cnblogs.com/minglee/p/10161899.html
Copyright © 2011-2022 走看看