精华内容
下载资源
问答
  • linux tcp服务器压力测试linux tcp服务器压力测试linux tcp服务器压力测试
  • linux tcpmodbus客户端

    2018-07-25 13:33:50
    linux tcpmodbus客户端 输入寄存器(0x04)离散输入寄存器(0x02)保持寄存器(0x03(读) 0x06(单个写) 0x10(多个写))
  • linux tcpmodbus服务端

    2018-07-25 09:49:48
    linux tcpmodbus服务端 输入寄存器(0x04)离散输入寄存器(0x02)保持寄存器(0x03(读) 0x06(单个写) 0x10(多个写))
  • linux tcp实例

    2013-02-28 17:41:54
    linux tcp实例 socket 阻塞和多路复用两中方式 实例
  • Linux TCP IP 协议栈分析

    2011-11-23 22:18:40
    Linux TCP IP 协议栈分析Linux TCP IP 协议栈分析Linux TCP IP 协议栈分析
  • linux TCP/IP协议源码

    2015-11-17 15:37:33
    linux TCP/IP协议源代码和详细的解析文档
  • Linux TCP_IP协议栈-高清

    2018-05-15 09:18:53
    Linux TCP_IP协议栈-高清,分享给所有需要的人.........
  • 追踪linux TCP/IP 代码运行_光盘文件2
  • Linux TCP完整代码

    2016-09-14 19:33:19
    linux环境下TCP编程实现,已通过真机测试
  • LinuxTCP客户端

    2017-09-25 11:29:16
    此代码是Linux环境下tcp通信的客户端,内容比较完善,并且经过多次测试,下载只需修改目标服务器的IP地址就行
  • 追踪LINUX TCP/IP代码运行,网络通信
  • Linux TCP 数据转发

    2018-06-08 12:12:06
    两个TCP端口之间互相转发,用在服务器上,跑在固定IP的云电脑上面,用任意IP地址去连接前端设备。
  • 深入浅出linux tcp_ip协议栈pdf格式清晰版 网上的那些要么是不清晰的,要么是png格式的。共四部分。
  • 深入浅出linux tcp_ip协议栈4

    热门讨论 2012-06-27 17:37:16
    深入浅出linux tcp_ip协议栈2 清晰版 共4部分要下就全部下。不然浪费分
  • 主要讲解的是linux内核下的拥塞控制机制,想对这方面了解的学生可以看看。
  • linux TCP IP协议栈源码解析资料大全
  • 深入浅出linux tcp_ip协议栈 清晰版 共4部分,要下就全部下,不然浪费分
  • TCP存活可以通过修改系统设置或者在tcp应用代码里面设置,本demo的是在tcp应用里面设置。原理是:对于一个已经建立的tcp连接。如果在keepalive_time时间内双方没有任何的数据包传输,则开启keepalive功能的一端将...
  • linux tcp 大量 TIME_WAIT 问题

    千次阅读 2018-01-24 12:08:40
    查看linux tcp连接状态发现存在大量 TIME_WAIT 状态连接 netstat -na | awk '{print $5,$6}'| sort | uniq -c | sort -n 结果: 2500 10.50.23.90:6379 TIME_WAIT 解决方法: sudo vim /etc/sysct

    阿里云服务器监控中发现tcp连接数监控异常,状态如下图:
    这里写图片描述

    查看linux tcp连接状态发现存在大量 TIME_WAIT 状态连接

    netstat -na  | awk '{print $5,$6}'| sort | uniq -c | sort -n
    
    结果:
    2500 10.50.23.90:6379 TIME_WAIT

    解决方法:

    
    sudo vim /etc/sysctl.conf
    编辑下面参数:
    
    net.ipv4.tcp_tw_recycle = 1
    net.ipv4.tcp_fin_timeout = 30
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_timestamps=1
    
    执行命令
    sudo sysctl -p  # 从配置文件“/etc/sysctl.conf”加载内核参数设置

    原理牵扯到 tcp连接终止协议的四次握手,参考文章:

    https://www.cnblogs.com/yjf512/p/5327886.html

    http://blog.sina.com.cn/s/blog_8e5d24890102w9yi.html

    http://blog.csdn.net/he_jian1/article/details/40787269

    sysctl参数说明:

    net.core.netdev_max_backlog = 400000
    #该参数决定了,网络设备接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。
    
    net.core.optmem_max = 10000000
    #该参数指定了每个套接字所允许的最大缓冲区的大小
    
    net.core.rmem_default = 10000000
    #指定了接收套接字缓冲区大小的缺省值(以字节为单位)。
    
    net.core.rmem_max = 10000000
    #指定了接收套接字缓冲区大小的最大值(以字节为单位)。
    
    net.core.somaxconn = 100000
    #Linux kernel参数,表示socket监听的backlog(监听队列)上限
    
    net.core.wmem_default = 11059200
    #定义默认的发送窗口大小;对于更大的 BDP 来说,这个大小也应该更大。
    
    net.core.wmem_max = 11059200
    #定义发送窗口的最大大小;对于更大的 BDP 来说,这个大小也应该更大。
    
    net.ipv4.conf.all.rp_filter = 1
    net.ipv4.conf.default.rp_filter = 1
    #严谨模式 1 (推荐)
    #松散模式 0
    
    net.ipv4.tcp_congestion_control = bic
    #默认推荐设置是 htcp
    
    net.ipv4.tcp_window_scaling = 0
    #关闭tcp_window_scaling
    #启用 RFC 1323 定义的 window scaling;要支持超过 64KB 的窗口,必须启用该值。
    
    net.ipv4.tcp_ecn = 0
    #把TCP的直接拥塞通告(tcp_ecn)关掉
    
    net.ipv4.tcp_sack = 1
    #关闭tcp_sack
    #启用有选择的应答(Selective Acknowledgment),
    #这可以通过有选择地应答乱序接收到的报文来提高性能(这样可以让发送者只发送丢失的报文段);
    #(对于广域网通信来说)这个选项应该启用,但是这会增加对 CPU 的占用。
    
    net.ipv4.tcp_max_tw_buckets = 10000
    #表示系统同时保持TIME_WAIT套接字的最大数量
    
    net.ipv4.tcp_max_syn_backlog = 8192
    #表示SYN队列长度,默认1024,改成8192,可以容纳更多等待连接的网络连接数。
    
    net.ipv4.tcp_syncookies = 1
    #表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
    
    net.ipv4.tcp_timestamps = 1
    #开启TCP时间戳 默认开启
    #以一种比重发超时更精确的方法(请参阅 RFC 1323)来启用对 RTT 的计算;为了实现更好的性能应该启用这个选项。
    
    net.ipv4.tcp_tw_reuse = 1
    #表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
    
    net.ipv4.tcp_tw_recycle = 1
    #表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
    
    net.ipv4.tcp_fin_timeout = 10
    #表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。
    
    net.ipv4.tcp_keepalive_time = 1800
    #表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为30分钟。
    
    net.ipv4.tcp_keepalive_probes = 3
    #如果对方不予应答,探测包的发送次数
    
    net.ipv4.tcp_keepalive_intvl = 15
    #keepalive探测包的发送间隔
    
    net.ipv4.tcp_mem
    #确定 TCP 栈应该如何反映内存使用;每个值的单位都是内存页(通常是 4KB)。
    #第一个值是内存使用的下限。
    #第二个值是内存压力模式开始对缓冲区使用应用压力的上限。
    #第三个值是内存上限。在这个层次上可以将报文丢弃,从而减少对内存的使用。对于较大的 BDP 可以增大这些值(但是要记住,其单位是内存页,而不是字节)。
    
    net.ipv4.tcp_rmem
    #与 tcp_wmem 类似,不过它表示的是为自动调优所使用的接收缓冲区的值。
    
    net.ipv4.tcp_wmem = 30000000 30000000 30000000
    #为自动调优定义每个 socket 使用的内存。
    #第一个值是为 socket 的发送缓冲区分配的最少字节数。
    #第二个值是默认值(该值会被 wmem_default 覆盖),缓冲区在系统负载不重的情况下可以增长到这个值。
    #第三个值是发送缓冲区空间的最大字节数(该值会被 wmem_max 覆盖)。
    
    net.ipv4.ip_local_port_range = 1024 65000
    #表示用于向外连接的端口范围。缺省情况下很小:32768到61000,改为1024到65000。
    
    net.ipv4.netfilter.ip_conntrack_max=204800
    #设置系统对最大跟踪的TCP连接数的限制
    
    net.ipv4.tcp_slow_start_after_idle = 0
    #关闭tcp的连接传输的慢启动,即先休止一段时间,再初始化拥塞窗口。
    
    net.ipv4.route.gc_timeout = 100
    #路由缓存刷新频率,当一个路由失败后多长时间跳到另一个路由,默认是300。
    
    net.ipv4.tcp_syn_retries = 1
    #在内核放弃建立连接之前发送SYN包的数量。
    
    net.ipv4.icmp_echo_ignore_broadcasts = 1
    # 避免放大攻击
    
    net.ipv4.icmp_ignore_bogus_error_responses = 1
    # 开启恶意icmp错误消息保护
    
    net.inet.udp.checksum=1
    #防止不正确的udp包的攻击
    
    net.ipv4.conf.default.accept_source_route = 0
    #是否接受含有源路由信息的ip包。参数值为布尔值,1表示接受,0表示不接受。
    #在充当网关的linux主机上缺省值为1,在一般的linux主机上缺省值为0。
    #从安全性角度出发,建议你关闭该功能。
    展开全文
  • 彻底实现Linux TCP的Pacing发送逻辑-高精度hrtimer版

    万次阅读 热门讨论 2017-01-14 08:36:17
     如果单纯的将《彻底实现Linux TCP的Pacing发送逻辑-普通timer版》中的timer_list换成hrtimer,必然招致失败。因为在hrtimer的function中,调用诸如tcp_write_xmit这样的长路径函数是一种用丝袜装榴莲的行为。好吧...
    代码的实现是简单的,背后的思绪是复杂的。
    
            如果单纯的将《 彻底实现Linux TCP的Pacing发送逻辑-普通timer版》中的timer_list换成hrtimer,必然招致失败。因为在hrtimer的function中,调用诸如tcp_write_xmit这样的长路径函数是一种用丝袜装榴莲的行为。好吧,在无奈中我只能参考TSQ的做法。旧恨心魔!
    在Linux的TCP实现中,TSQ保证了一个单独的流不会过多地占据发送缓存,从而保证的多个数据流的相对公平。这个机制是用tasklet实现的,那么我觉得TCP的pacing也能通过tasklet来实现。
    -------------------------------------
    TCP毁了整个网络世界的和谐!Why?
            是谁说TCP发送端一定要维护一个拥塞窗口了?这人树立了权威!然而“拥塞窗口”这个概念只说明了事情的一个方面而对另一个方面只字未提!我说过,拥塞窗口是一个标量而不是一个矢量,这样说的含义在于,它仅仅是在一个维度上度量的一个数值而已,表示“目前可以发送的数据量”,仅此而已。拥塞窗口根本不懂网络上发生了什么。不过可以肯定的是,接收端观察到的数据到达行为是真实的,即两个数据包到达之间是有间隔的!如果你分别在发送端和接收端抓包并分析,将会很容易看到这个事实。
            我们再看发送端,以Linux为例,其发送行为是tcp_write_xmit主持的,它会一次性发送所有可供发送的数据包,每个数据包之间的延迟仅仅是数据包走一趟协议栈的主机延迟,这种延迟对于长肥网络延迟是可以忽略不计的!请我们不要被千兆/万兆以太网以及DC内部的假象所蒙蔽,我们在互联网上访问的内容大多数来自“千里之外”,中间的路途崎岖坎坷,不要相信什么CDN,全都扯淡,商业宣传的噱头,没个鸟用。回到正文,既然发送端是一次性突发发送的数据包,而接收端是间歇性接收到数据包,中间一定发生了什么!是的,这就是根本,但是我们不用关心这个,只要好好跪舔TCP就好了。我们注意一种类似的现象,那就是结婚的婚车,接新娘出发的时候,几十辆德系BBA(奔驰,宝马,奥迪)由一辆“超级豪车”(比如改装的宾利)领衔,一字排开列队,集体统一出发,可是在快到达新娘子家的时候,头车必须停下来等待后车,以营造一种一路上队形持续保持并且今天太阳为你升起的假象,这就是流量整形的作用,首先,车队经由的道路是一个统计复用的系统,并没有卖给哪个个人,加上路上红绿灯,插队,交通拥堵等因素,车队会被打散,本来前后车仅仅相隔几米,最终这个距离会拉开到以公里计算,快要到达目的地的时候,为了以出发时的阵容到达,就必须等待所有的车辆到齐,然后一字排开进入新娘子家。其实这些在网卡数据传输技术中,都有对应的东西,比如最后那个等车到齐的行为,其实就是LRO(large-receive-offload)或者分片重组之类的。
            我要说的不是这个,我要说的是,你知道你结个婚搞这么个车队,给交通带来多大影响吗?!我们假设所有人都是毫无感性的,谁也不让谁,但你能想象这么大的一字车队从一个小巷子里开出的场景吗?令人遗憾的是,我们的互联网上几乎每一台可以发送数据的主机(不管是DC的机器,还是你我的电脑),每时每刻都在有这么大阵容的车队出发!难道就不能互相谦让一下,在起码在两车之间拉开一辆车的距离,至少过十字路口的时候,有别的车辆可以交叉通过啊!但事实上,在中国,傻逼才会这么做,劣币驱良币,因为没人这么做!大家都恨不得让车队更长些呢!
            在网络上,虽然TCP的拥塞窗口指示了可以发送多少数据,但是为什么不能按照接收端实际接收的行为来指导发送行为呢?为什么几十年来都是一次性发送并依然如故呢?至少Linux的TCP是这样的,我相信别的也好不到哪去,毕竟“作恶的,必被剪除”只是一个心愿。劣币驱良币,大家都作恶,恶就成了善。
            这就是TCP的悲哀!Google的工程师看到了这种悲哀,造出了BBR算法,引无数人跪舔,这更悲哀。Google的BBR patch如是说:
    The primary control is the pacing rate: BBR applies a gain
    multiplier to transmit faster or slower than the observed bottleneck
    bandwidth. The conventional congestion window (cwnd) is now the
    secondary control; the cwnd is set to a small multiple of the
    estimated BDP (bandwidth-delay product) in order to allow full
    utilization and bandwidth probing while bounding the potential amount
    of queue at the bottleneck.

    总结一下本段的抱怨。只用拥塞窗口控制TCP的发送行为是一个垃圾方式,都是鸡屎。有破才有立,我决定实现Linux TCP pacing的hrtimer版了。
    -------------------------------------
    总的框架如下:




    看起来,前面实现的常规timer版本的TCP pacing仅仅是一个引子,本文将要实现的基于tasklet的hrtimer版本才是王道!上述框架理解了之后,实现起来就是信手拈来的事了。非常之简单。
    -------------------------------------
    还是跟普通timer版一样,我把代码拆解成几个部分列如下,然而这并不是代码的全部,我省掉了一些诸如list_head初始化的代码,以及一些变量初始化的代码。

    1.tasklet的实现:

    // 定义pacing_tasklet:
    /* include/net/tcp.h */
    struct pacing_tasklet {
            struct tasklet_struct   tasklet;
            struct list_head        head; /* queue of tcp sockets */
    };
    extern struct pacing_tasklet pacing_tasklet;
    
    /* net/ipv4/tcp_output.c */
    
    // 定义per cpu的tasklet变量
    DEFINE_PER_CPU(struct pacing_tasklet, pacing_tasklet);
    
    // 独立出来的handler,仅仅为了与tasklet的action分离,使其不至于太长
    static void tcp_pacing_handler(struct sock *sk)
    {
            struct tcp_sock *tp = tcp_sk(sk);
    
            if(!sysctl_tcp_pacing || !tp->pacing.pacing)
                    return ;
    
            if (sock_owned_by_user(sk)) {
                    if (!test_and_set_bit(TCP_PACING_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags))
                            sock_hold(sk);
                    goto out;
            }
    
            if (sk->sk_state == TCP_CLOSE)
                    goto out;
    
            if(!sk->sk_send_head){
                    goto out;
            }
    
            tcp_push_pending_frames(sk);
    
    out:
            if (tcp_memory_pressure)
                    sk_mem_reclaim(sk);
    }
    // pacing tasklet的action函数
    static void tcp_pacing_func(unsigned long data)
    {
            struct pacing_tasklet *pacing = (struct pacing_tasklet *)data;
            LIST_HEAD(list);
            unsigned long flags;
            struct list_head *q, *n;
            struct tcp_sock *tp;
            struct sock *sk;
    
            local_irq_save(flags);
            list_splice_init(&pacing->head, &list);
            local_irq_restore(flags);
    
            list_for_each_safe(q, n, &list) {
                    tp = list_entry(q, struct tcp_sock, pacing_node);
                    list_del(&tp->pacing_node);
    
                    sk = (struct sock *)tp;
                    bh_lock_sock(sk);
    
                    tcp_pacing_handler(sk);
                    bh_unlock_sock(sk);
    
                    clear_bit(PACING_QUEUED, &tp->tsq_flags);
            }
    }
    // 初始化pacing tasklet(完全学着tsq的样子来做)
    void __init tcp_tasklet_init(void)
    {
            int i,j;
    
            struct sock *sk;
    
            local_irq_save(flags);
            list_splice_init(&pacing->head, &list);
            local_irq_restore(flags);
    
            list_for_each_safe(q, n, &list) {
                    tp = list_entry(q, struct tcp_sock, pacing_node);
                    list_del(&tp->pacing_node);
            
                    sk = (struct sock *)tp;
                    bh_lock_sock(sk);
    
                    tcp_pacing_handler(sk);
                    bh_unlock_sock(sk);
    
                    clear_bit(PACING_QUEUED, &tp->tsq_flags);
            }
    }
    

    2.hrtimer相关:
    /* net/ipv4/tcp_timer.c */
    
    // 重置hrtimer定时器
    void tcp_pacing_reset_timer(struct sock *sk, u64 expires)
    {
            struct tcp_sock *tp = tcp_sk(sk);
            u32 timeout = nsecs_to_jiffies(expires);
    
            if(!sysctl_tcp_pacing || !tp->pacing.pacing)
                    return;
    
            hrtimer_start(&sk->timer,
                          ns_to_ktime(expires),
                          HRTIMER_MODE_ABS_PINNED);
    }
    // hrtimer的超时回调
    static enum hrtimer_restart tcp_pacing_timer(struct hrtimer *timer)
    {
            struct sock *sk = container_of(timer, struct sock, timer);
            struct tcp_sock *tp = tcp_sk(sk);
    
            if (!test_and_set_bit(PACING_QUEUED, &tp->tsq_flags)) {
                    unsigned long flags;
                    struct pacing_tasklet *pacing;
                    // 仅仅调度起tasklet,而不是执行action!
                    local_irq_save(flags);
                    pacing = this_cpu_ptr(&pacing_tasklet);
                    list_add(&tp->pacing_node, &pacing->head);
                    tasklet_schedule(&pacing->tasklet);
                    local_irq_restore(flags);
            }
            return HRTIMER_NORESTART;
    }
    // 初始化
    void tcp_init_xmit_timers(struct sock *sk)
    {
            inet_csk_init_xmit_timers(sk, &tcp_write_timer, &tcp_delack_timer,
                                      &tcp_keepalive_timer);
            hrtimer_init(&sk->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
            sk->timer.function = &tcp_pacing_timer;
    }
    3.tcp_write_xmit中的判断:
    /* net/ipv4/tcp_output.c */
    static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
                               int push_one, gfp_t gfp)
    {
            ...
            while ((skb = tcp_send_head(sk))) {
                    unsigned int limit;
                    u64 now = ktime_get_ns();
    
                    ...
    
                    cwnd_quota = tcp_cwnd_test(tp, skb);
                    if (!cwnd_quota) {
                            if (push_one == 2)
                                    /* Force out a loss probe pkt. */
                                    cwnd_quota = 1;
                            else if(tp->pacing.pacing == 0) // 这里是个创举,既然pacing rate就是由cwnd算出来,检查了pacing rate就不必再检测cwnd了,但是在bbr算法中要慎重,因为bbr的pacing rate真不是由cwnd算出来的,恰恰相反,cwnd是由pacing算出来的!
                                    break;
                    }
                    // 通告窗口与网络拥塞无关,还是要检测的。
                    if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now)))
                            break;
                    // 这里的逻辑与普通timer版的一样!
                    if (sysctl_tcp_pacing && tp->pacing.pacing == 1) {
                            u32 plen;
                            u64 rate, len;
    
                            if (now < tp->pacing.next_to_send) {
                                    tcp_pacing_reset_timer(sk, tp->pacing.next_to_send);
                                    break;
                            }
    
                            rate = sysctl_tcp_rate ? sysctl_tcp_rate:sk->sk_pacing_rate;
                            plen = skb->len + MAX_HEADER;
                            len = (u64)plen * NSEC_PER_SEC;
                            if (rate)
                                    do_div(len, rate);
                            tp->pacing.next_to_send = now + len;
                            if (cwnd_quota == 0)
                                    cwnd_quota = 1;
                    }
    
                    if (tso_segs == 1) {
            ...
    }

    4.tcp_release_cb中执行
    /* net/ipv4/tcp_output.c */
    void tcp_release_cb(struct sock *sk)
    {
            ...
            if (flags & (1UL << TCP_PACING_TIMER_DEFERRED)) {
                    if(sk->sk_send_head) {
                            tcp_push_pending_frames(sk);
                    }
                    __sock_put(sk);
            }
            ...
    }

    以上4个部分就是几乎全部的逻辑了。
    -------------------------------------
    现在看看效果,使用netperf的结果我就不贴了,我只贴一个使用curl下载10M文件的对比结果。

    首先看标准cubic算法的曲线:


    CTMB,垃圾!都他妈的是垃圾!


    其吞吐量曲线如下图所示




    然后再看我的pacing曲线:

    然后再看看吞吐量的图!我虽然没有上过大学,其实我也是不屑于大学的,我的圈子里,都是硕博连读的,好久不回一次国,而我,不知本科为何?!那么看看结果吧:


    -------------------------------------

    最后看看我最初的愿景。
            我的想法并不是要在TCP上搞什么pacing,整个TCP,如果你想改变点什么的话,在中国就是个丑行,因为中国人根本不懂真正的博弈。实际上我的目标是UDP之上承载的VPN流量!因为相比一个普通的TCP数据包,一个VPN数据包被丢掉的代价太大了。这部仅仅意味着网络带宽被浪费,由于重传还会把CPU拉入泥潭!上周写了一个基于DTLS的VPN,在我的测试中,CPU一直飙高,后来查出来是因为丢包太严重导致,然后在用户态实现了一个pacing发送,问题就解决了。
            UDP当然可以在用户态实现pacing,而TCP却不能,因为TCP的发送并不受用户的控制,所以就想到了这个方案并简单实现了个Demo。然而多多少少让人觉得我身在曹营心在汉,其实则不然,我其实身在曹营心也在曹营,只是哀其不幸,而怒其不争。

    展开全文
  • Linux TCP简单聊天程序

    2013-06-25 17:19:03
    Linux 系统下实现的TCP简单聊天程序,亲测可用!
  • The Linux TCP IP Stack Networking for Embedded Systems.rar
  • linux TCP超时重传

    万次阅读 2013-06-04 11:16:48
    TCP超时重传是保证TCP可靠性传输的机制之一,当超时后...linux TCP超时重传是通过设置重传超时时钟icsk_retransmit_timer来实现的。 零窗探测超时时钟与重传超时时钟共用icsk_retransmit_timer,根据icsk_pending是IC

    TCP超时重传是保证TCP可靠性传输的机制之一,当超时后仍没有收到应答报文,就重传数据包并设置超时时钟(超时时间一般增大到原超时时间2倍);直到收到应答报文或超过最大重试次数。


    linux TCP超时重传是通过设置重传超时时钟icsk_retransmit_timer来实现的。
    零窗探测超时时钟与重传超时时钟共用icsk_retransmit_timer,根据icsk_pending是ICSK_TIME_RETRANS、ICSK_TIME_PROBE0来判断是重传超时还是零窗探测超时。
    只有当发送方被通告零窗,连接双方没有数据来往而使接收方无法通过ACK报文通告新窗口时,才使用零窗探测机制;所以重传队列中有重传包时,不会出现零窗探测,出现零窗时不能再发送新数据也就没有重传;所以零窗探测超时时钟与重传超时时钟可以共用icsk_retransmit_timer


    I.超时处理函数

    i.tcp_retransmit_timer

    281 /*
    282  *      The TCP retransmit timer.
    283  */
    284 
    285 void tcp_retransmit_timer(struct sock *sk)
    286 {
    287         struct tcp_sock *tp = tcp_sk(sk);
    288         struct inet_connection_sock *icsk = inet_csk(sk);
    289 
    290         if (!tp->packets_out)
    291                 goto out;
    292 
    293         WARN_ON(tcp_write_queue_empty(sk));
    294 
    295         if (!tp->snd_wnd && !sock_flag(sk, SOCK_DEAD) &&
    296             !((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))) {
    297                 /* Receiver dastardly shrinks window. Our retransmits
    298                  * become zero probes, but we should not timeout this
    299                  * connection. If the socket is an orphan, time it out,
    300                  * we cannot allow such beasts to hang infinitely.
    301                  */
    302 #ifdef TCP_DEBUG
    303                 struct inet_sock *inet = inet_sk(sk);
    304                 if (sk->sk_family == AF_INET) {
    305                         LIMIT_NETDEBUG(KERN_DEBUG "TCP: Peer %pI4:%u/%u unexpectedly shrunk window %u:%u (repaired)\n",
    306                                &inet->daddr, ntohs(inet->dport),
    307                                inet->num, tp->snd_una, tp->snd_nxt);
    308                 }
    309 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
    310                 else if (sk->sk_family == AF_INET6) {
    311                         struct ipv6_pinfo *np = inet6_sk(sk);
    312                         LIMIT_NETDEBUG(KERN_DEBUG "TCP: Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n",
    313                                &np->daddr, ntohs(inet->dport),
    314                                inet->num, tp->snd_una, tp->snd_nxt);
    315                 }
    316 #endif
    317 #endif
    318                 if (tcp_time_stamp - tp->rcv_tstamp > TCP_RTO_MAX) {
    319                         tcp_write_err(sk);
    320                         goto out;
    321                 }
    322                 tcp_enter_loss(sk, 0);
    323                 tcp_retransmit_skb(sk, tcp_write_queue_head(sk));
    324                 __sk_dst_reset(sk);
    325                 goto out_reset_timer;
    326         }
    327 
    328         if (tcp_write_timeout(sk))
    329                 goto out;
    330 
    331         if (icsk->icsk_retransmits == 0) {
    332                 int mib_idx;
    333 
    334                 if (icsk->icsk_ca_state == TCP_CA_Disorder) {
    335                         if (tcp_is_sack(tp))
    336                                 mib_idx = LINUX_MIB_TCPSACKFAILURES;
    337                         else
    338                                 mib_idx = LINUX_MIB_TCPRENOFAILURES;
    339                 } else if (icsk->icsk_ca_state == TCP_CA_Recovery) {
    340                         if (tcp_is_sack(tp))
    341                                 mib_idx = LINUX_MIB_TCPSACKRECOVERYFAIL;
    342                         else
    343                                 mib_idx = LINUX_MIB_TCPRENORECOVERYFAIL;
    344                 } else if (icsk->icsk_ca_state == TCP_CA_Loss) {
    345                         mib_idx = LINUX_MIB_TCPLOSSFAILURES;
    346                 } else {
    347                         mib_idx = LINUX_MIB_TCPTIMEOUTS;
    348                 }
    349                 NET_INC_STATS_BH(sock_net(sk), mib_idx);
    350         }
    351 
    352         if (tcp_use_frto(sk)) {
    353                 tcp_enter_frto(sk);
    354         } else {
    355                 tcp_enter_loss(sk, 0);
    356         }
    357 
    358         if (tcp_retransmit_skb(sk, tcp_write_queue_head(sk)) > 0) {
    359                 /* Retransmission failed because of local congestion,
    360                  * do not backoff.
    361                  */
    362                 if (!icsk->icsk_retransmits)
    363                         icsk->icsk_retransmits = 1;
    364                 inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
    365                                           min(icsk->icsk_rto, TCP_RESOURCE_PROBE_INTERVAL),
    366                                           TCP_RTO_MAX);
    367                 goto out;
    368         }
    369 
    370         /* Increase the timeout each time we retransmit.  Note that
    371          * we do not increase the rtt estimate.  rto is initialized
    372          * from rtt, but increases here.  Jacobson (SIGCOMM 88) suggests
    373          * that doubling rto each time is the least we can get away with.
    374          * In KA9Q, Karn uses this for the first few times, and then
    375          * goes to quadratic.  netBSD doubles, but only goes up to *64,
    376          * and clamps at 1 to 64 sec afterwards.  Note that 120 sec is
    377          * defined in the protocol as the maximum possible RTT.  I guess
    378          * we'll have to use something other than TCP to talk to the
    379          * University of Mars.
    380          *
    381          * PAWS allows us longer timeouts and large windows, so once
    382          * implemented ftp to mars will work nicely. We will have to fix
    383          * the 120 second clamps though!
    384          */
    385         icsk->icsk_backoff++;
    386         icsk->icsk_retransmits++;
    387 
    388 out_reset_timer:
    389         icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
    390         inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);
    391         if (retransmits_timed_out(sk, sysctl_tcp_retries1 + 1))
    392                 __sk_dst_reset(sk);
    393 
    394 out:;
    395 }

    1.如果是零窗且不是握手包(零窗时仍能发送SYN/SYN-ACK包,由于不包含数据)
      a.检查保活时间上限,如果超过上限,则表示TCP连接已经断开,通知上层应用
      b.进入Loss拥塞状态
      c.重传队列首包重传
      d.重置路由缓存
      e.设置超时时钟,如果重传发送成功重传时间增大2倍(以避免拥塞),如果重传发送失败重传时间不变
    2.如果是零窗且且是握手包,或非零窗
      a.根据sysctl配置,检查重传次数是否已经超过上限,如果超过上限说明连接异常断开,通知上层应用
      b.如果是第一次重传,则统计MIB信息
      c.进入Loss拥塞状态或Disorder拥塞状态
      d.重传队列首包重传
      e.设置超时时钟,如果重传发送成功重传时间增大2倍(以避免拥塞),如果重传发送失败重传时间不变

     

    ii.tcp_write_timeout
    检测TCP连接异常断开机制有,没有数据发送时的保活机制(默认保活超时为sysctl_tcp_keepalive_time=7200s=2小时),有数据发送时的重传超限等
    tcp_write_timeout主要用于检测重传次数超限,如果超限后但仍未成功,则表示该连接已经异常断开。
    握手时SYN与SYN-ACK的重传上限由sysctl_tcp_syn_retries设置;数据发送的重传上限由sysctl_tcp_retries2设置
    sysctl_tcp_syn_retries、sysctl_tcp_retries2严格来讲不是重传次数上限,而是用来衡量超限时间距包第一次重传时间或包发送时间的时间间隔。
    重传超限检测函数retransmits_timed_out

    1286 /* This function calculates a "timeout" which is equivalent to the timeout of a
    1287  * TCP connection after "boundary" unsucessful, exponentially backed-off
    1288  * retransmissions with an initial RTO of TCP_RTO_MIN.
    1289  */
    1290 static inline bool retransmits_timed_out(struct sock *sk,
    1291                                          unsigned int boundary)
    1292 {
    1293         unsigned int timeout, linear_backoff_thresh;
    1294         unsigned int start_ts;
    1295 
    1296         if (!inet_csk(sk)->icsk_retransmits)
    1297                 return false;
    1298 
    1299         if (unlikely(!tcp_sk(sk)->retrans_stamp))
    1300                 start_ts = TCP_SKB_CB(tcp_write_queue_head(sk))->when;
    1301         else
    1302                 start_ts = tcp_sk(sk)->retrans_stamp;
    1303 
    1304         linear_backoff_thresh = ilog2(TCP_RTO_MAX/TCP_RTO_MIN);
    1305 
    1306         if (boundary <= linear_backoff_thresh)
    1307                 timeout = ((2 << boundary) - 1) * TCP_RTO_MIN;
    1308         else
    1309                 timeout = ((2 << linear_backoff_thresh) - 1) * TCP_RTO_MIN +
    1310                           (boundary - linear_backoff_thresh) * TCP_RTO_MAX;
    1311 
    1312         return (tcp_time_stamp - start_ts) >= timeout;
    1313 }

    start_ts:第一次重传时间或发送时间
    TCP_RTO_MIN=(HZ/5)=0.2s
    TCP_RTO_MAX=(120*HZ)=120s
    linear_backoff_thresh = ilog2(120*5)=ilog2(0x258)=9
    timeout:未超过linear_backoff_thresh=9的部分按TCP_RTO_MIN 2的指数倍增长,超过的部分按TCP_RTO_MAX线性增长
    tcp_time_stamp:当前时钟时间
    例如数据发送阶段,sysctl_tcp_retries2=9,则timeout=1023*TCP_RTO_MIN=204.6s;sysctl_tcp_retries2=11时,timeout=1023*TCP_RTO_MIN+2*TCP_RTO_MAX=448.6s
    默认sysctl_tcp_retries2=15,timeout=1023*TCP_RTO_MIN+6*TCP_RTO_MAX=920.6s,约15分钟

     


    II.超时时间
    TCP超时时间初始值是根据RTT时间计算得到,随着重传次数增加重传时间间隔也增加,如
    发送包1->超时重传包1->超时重传包1->超时重传包1->收到ack包1->超时重传包2->超时重传包2
    的超时时间变化如下:
    a      ->2a         ->4a         ->8a         ->a         ->2a         ->4a

     

    i.超时时间初始值
    ack新数据时,都会重新计算RTT,此时也会根据新的RTT计算超时时间
    tcp_clean_rtx_queue->tcp_ack_update_rtt->tcp_ack_saw_tstamp/tcp_ack_saw_tstamp->tcp_valid_rtt_meas->tcp_set_rto
    linux TCP的采用平滑RTT计算方式,函数为tcp_rtt_estimator
    srtt:平滑的rtt,新rtt平滑值 = 7/8老rtt平滑值 + 1/8新值
    mdev:平滑的rtt差值,用来衡量rtt的抖动情况,新mdev平滑值 = 3/4老mdev平滑值 + 1/4新值
    mdev_max:上一个RTT内的最大mdev,代表上个RTT内时延的波动情况,有效期为一个RTT
    rttvar:mdev_max的平滑值,可升可降,代表着连接的抖动情况

    超时时间初始值=__tcp_set_rto()=srtt/8 + rttvar(除8是因为将RTT扩大8倍存储在srtt中);

     

    ii.重传超时加倍
    重传失败后,超时时间间隔加倍后,再次重传;

     


    III.重传超时时钟注册,启用,停止与修改
    当TCP连接建立时,注册重传超时时钟处理函数
    当重传队列为空,有新数据发送时,启用超时时钟;
    当收到ACK报文,重传队列中的数据全都确认后,停止超时时钟;
    当收到ACK报文,但是重传队列仍有数据时,修改超时时钟为初始值(根据平滑RTT计算),作为下一个待重传包的超时。
    注:重传时钟超时只重传重传队列的首包;此时重传队列数据包的ACK报文都在拥塞状态机Loss状态下处理。

     

    i.重传超时时钟处理函数注册
    在TCP主动连接或被动连接时初始化超时时钟,即发送SYN或收到SYN时初始化(linux实现是,在收到SYN报文创建request_sock,收到ACK时创建子sock,在创建子sock时初始化超时时钟)
    发送SYN:
    socket->inet_create->tcp_v4_init_sock->tcp_init_xmit_timers->inet_csk_init_xmit_timers
    收到ACK:
    tcp_v4_rcv->tcp_v4_do_rcv->tcp_v4_hnd_req->tcp_check_req->tcp_v4_syn_recv_sock->tcp_create_openreq_child->tcp_init_xmit_timers->inet_csk_init_xmit_timers

     

    ii.超时时钟启用
    发送数据时启用超时时钟:如果没有未被确认的报文,即重传队列为空,则启用超时时钟
    tcp_write_xmit->tcp_event_new_data_sent->inet_csk_reset_xmit_timer->sk_reset_timer->mod_timer
    注:mod_timer激活时钟,或修改已经激活时钟的过期时间;此处为激活超时时钟

     

    iii.重传超时时钟停止
    收到ACK报文,确认重传队列中所有报文时,停止重传超时时钟
    tcp_ack->tcp_clean_rtx_queue->tcp_rearm_rto->inet_csk_clear_xmit_timer->icsk_pending=0

     

    iv.重传超时时钟修改
    收到ACK报文,但是重传队列中仍有重传包,则重置重传超时时钟:
    tcp_ack->tcp_clean_rtx_queue->tcp_rearm_rto->inet_csk_reset_xmit_timer->sk_reset_timer->mod_timer
    注:mod_timer激活时钟,或修改已经激活时钟的过期时间;此处为修改已经激活时钟的过期时间

     


    IV.重传队列
    重传队列如图:


    sk_write_queue->next:用于标识重传队列首skb
    sk_send_head:用于标识重传队列尾下一个skb,发送缓存数据的起始skb,在tcp_add_write_queue_tail赋初始值,在tcp_event_new_data_sent向前移动。在send系统调用中,当因拥塞或流量控制而无法直接发送出去时,会将数据放入发送缓存中,在收到ACK报文后tcp_data_snd_check会对缓存数据再次尝试发送;如果缓存数据仍不能发送成功,且重传队列中无数据时,会启用零窗探测时钟以保证缓存数据一定可以发送出去(如果发送缓存中有数据,零窗探测会发送缓存数据,否则会发送零窗探测报文,则保证一定能够收到ACK报文)。
    packets_out:表示重传队列中数据长度,在tcp_event_new_data_sent中增大

    展开全文
  • Linux TCP Tuning(Tcp优化)

    千次阅读 2007-09-20 02:29:00
    原贴:http://blog.chinajavaworld.com/entry.jspa?id=1182 Linux TCP Tuning There are a lot of differences between Linux version 2.4 and 2.6, so first well cover the tuning i
    原贴:http://blog.chinajavaworld.com/entry.jspa?id=1182
    

    Linux TCP Tuning
    There are a lot of differences between Linux version 2.4 and 2.6, so first we'll cover the tuning issues that are the same in both 2.4 and 2.6. To change TCP settings in, you add the entries below to the file /etc/sysctl.conf, and then run "sysctl -p".

    Like all operating systems, the default maximum Linux TCP buffer sizes are way too small. I suggest changing them to the following settings:

    1
    2
    3
    4
    5
    6
    7
      # increase TCP max buffer size
    net.core.rmem_max = 16777216
    net.core.wmem_max = 16777216
    # increase Linux autotuning TCP buffer limits
    # min, default, and max number of bytes to use
    net.ipv4.tcp_rmem = 4096 87380 16777216
    net.ipv4.tcp_wmem = 4096 65536 16777216

    Note: you should leave tcp_mem alone. The defaults are fine.

    Another thing you can try that may help increase TCP throughput is to increase the size of the interface queue. To do this, do the following:
    1
         ifconfig eth0 txqueuelen 1000 

    I've seen increases in bandwidth of up to 8x by doing this on some long, fast paths. This is only a good idea for Gigabit Ethernet connected hosts, and may have other side effects such as uneven sharing between multiple streams.


    --------------------------------------------------------------------------------
    Linux 2.4
    Starting with Linux 2.4, Linux has implemented a sender-side autotuning mechanism, so that setting the opitimal buffer size on the sender is not needed. This assumes you have set large buffers on the recieve side, as the sending buffer will not grow beyond the size of the recieve buffer.

    However, Linux 2.4 has some other strange behavior that one needs to be aware of. For example: The value for ssthresh for a given path is cached in the routing table. This means that if a connection has has a retransmition and reduces its window, then all connections to that host for the next 10 minutes will use a reduced window size, and not even try to increase its window. The only way to disable this behavior is to do the following before all new connections (you must be root):

    sysctl -w net.ipv4.route.flush=1
    More information on various tuning parameters for Linux 2.4 are available in the Ipsysctl tutorial .


    --------------------------------------------------------------------------------
    Linux 2.6
    Starting in Linux 2.6.7 (and back-ported to 2.4.27), BIC TCP is part of the kernel, and enabled by default. BIC TCP helps recover quickly from packet loss on high-speed WANs, and appears to work quite well. A BIC implementation bug was discovered, but this was fixed in Linux 2.6.11, so you should upgrade to this version or higher.

    Linux 2.6 also includes and both send and receiver-side automatic buffer tuning (up to the maximum sizes specified above). There is also a setting to fix the ssthresh caching weirdness described above.

    There are a couple additional sysctl settings for 2.6:
    1
    2
    3
    4
    5
    6
       # don't cache ssthresh from previous connection
    net.ipv4.tcp_no_metrics_save = 1
    # recommended to increase this for 1000 BT or higher
    net.core.netdev_max_backlog = 2500
    # for 10 GigE, use this
    # net.core.netdev_max_backlog = 30000

    Starting with version 2.6.13, Linux supports pluggable congestion control algorithms . The congestion control algorithm used is set using the sysctl variable net.ipv4.tcp_congestion_control, which is set to Reno by default. (Apparently they decided that BIC was not quite ready for prime time.) The current set of congestion control options are:

    reno: Traditional TCP used by almost all other OSes. (default)
    bic: BIC-TCP
    highspeed: HighSpeed TCP: Sally Floyd's suggested algorithm
    htcp: Hamilton TCP
    hybla: For satellite links
    scalable: Scalable TCP
    vegas: TCP Vegas
    westwood: optimized for lossy networks
    For very long fast paths, I suggest trying HTCP or BIC-TCP if Reno is not is not performing as desired. To set this, do the following:


    sysctl -w net.ipv4.tcp_congestion_control=htcp
    More information on each of these algorithms and some results can be found here .

    Note: Linux 2.6.11 and under has a serious problem with certain Gigabit and 10 Gig ethernet drivers and NICs that support "tcp segmentation offload", such as the Intel e1000 and ixgb drivers, the Broadcom tg3, and the s2io 10 GigE drivers. This problem was fixed in version 2.6.12. A workaround for this problem is to use ethtool to disable segmentation offload:

    ethtool -K eth0 tso off
    This will reduce your overall performance, but will make TCP over LFNs far more stable.
    More information on tuning parameters and defaults for Linux 2.6 are available in the file ip-sysctl.txt, which is part of the 2.6 source distribution.

    And finally a warning for both 2.4 and 2.6: for very large BDP paths where the TCP window is > 20 MB, you are likely to hit the Linux SACK implementation problem. If Linux has too many packets in flight when it gets a SACK event, it takes too long to located the SACKed packet, and you get a TCP timeout and CWND goes back to 1 packet. Restricting the TCP buffer size to about 12 MB seems to avoid this problem, but clearly limits your total throughput. Another solution is to disable SACK.


    --------------------------------------------------------------------------------
    Linux 2.2
    If you are still running Linux 2.2, upgrade! If this is not possible, add the following to /etc/rc.d/rc.local

    echo 8388608 > /proc/sys/net/core/wmem_max
    echo 8388608 > /proc/sys/net/core/rmem_max
    echo 65536 > /proc/sys/net/core/rmem_default
    echo 65536 > /proc/sys/net/core/wmem_default

     
    展开全文
  • Linux TCP/IP 协议栈源码分析

    千次阅读 2017-01-12 10:09:09
    [转载]Linux TCP/IP 协议栈源码分析 一.linux内核网络栈代码的准备知识 1. linux内核ipv4网络部分分层结构: BSD socket层: 这一部分处理BSD socket相关操作,每个socket在内核中以struct socket结构体现。这一...
  • Linux 内核 TCP/IP 网络架构设计与实现细节,经典教材!
  • linux tcp 循环发数据

    2011-02-23 14:19:53
    linux 利用tcp 循环发数据,附有代码和说明
  • linux tcp udp 调试工具

    热门讨论 2015-10-07 10:43:32
    sokit 是一款开源免费的 TCP / UDP 测试(调试)工具, 可以用来接收,发送或转发TCP/UDP数据包。 本程序可以工作在三种模式: 服务器模式,用来监听本地端口,接收外部数据包,并且可以回复自定义数据; 客户端模式,...
  • Linux TCP/IP 协议栈调优

    千次阅读 2014-12-04 16:00:11
    有些性能瓶颈和LinuxTCP/IP的协议栈的设置有关,所以特别google了一下Linux TCP/IP的协议栈的参数意义和配置,记录一下。 如果想永久的保存参数的设置, 可以将参数加入到/etc/sysctl.conf中。如果想临时的更改...
  • NS2中的Linux TCP实现

    千次阅读 2013-09-02 18:55:08
    A Linux TCP implementation for NS2 (Part of the NS-2 enhancement project) David X. Wei   Prof. Pei Cao Netlab @ Caltech   CS @ Stanford May 2006; Revision 1 for ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 512,221
精华内容 204,888
关键字:

linuxtcp

linux 订阅