Linux tcp timewait相处之道

2024-09-06 08:38
文章标签 linux tcp 之道 timewait 相处

本文主要是介绍Linux tcp timewait相处之道,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

关于Time wait 的特殊细节

熟悉tcp网络编程的同学对于timewait 状态可以说是既熟悉又陌生。在繁忙的server端,该状态经常会使得server无法bind,或者耗尽可用的port资源。此时此刻,心里往往不知所以,万般无奈。本文结合实验,spec和代码原理,解释了timewait 的几种处理方案。

参考文献
1) unix networking programming
2) Coping with the TCP TIME-WAIT state on busy Linux servers https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux
3) stackoverflow: SO_REUSEADDR vs SO_REUSEPORT

下文中使用的sock程序是从unix networking programming来的,下载地址 https://github.com/unpbook/unpv13e

SO_REUSEADDR和Timewait的关系

  • 即使enable了SO_REUSEADDR,timewait 状态的(src ip, src port, dst ip, dst port) 也不能被connect复用。

证明步骤来自Richard steven Unix network programming 的例子:

  1. 在server上执行 ./sock -s 0.0.0.0 80

  2. 在client 上执行 ./sock 10.0.0.17 80

  3. stop client

观察socket pair 处于tw 状态

tcp        0      0 10.0.0.4:46210          10.0.0.17:80    TIME_WAIT   -
  1. 复用timewait 状态的source port 去连接server,结果失败。
    ./sock -b 46210 10.0.0.17 80bind() error: Address already in use
  1. 使用SO_REUSEADDR, 复用tw 状态的source port 去连接server,结果同样失败。
./sock -A -b 47480 10.0.0.17 80
bind() error: Address already in use
  • Enable 这个选项,使得timewait状态的(src ip, src port)可以bind,否则bind会失败。另外client 能够connect 该地址,从而可能导致timewait 五元祖被复用。这似乎是一个bug。

实验步骤:

  1. 在server上监听80端口, ./sock -s 10.0.0.17 80
  2. 在client上连接server的80端口 ./sock -b 1234 10.0.0.17 80
  3. server 主动断开连接,使得socket 进入timewait状态
  4. server 再次监听, ./sock -s 10.0.0.17 80 结果会失败
./sock  -s 0.0.0.0 80
can't bind local address: Address already in use
  1. server 设置 SO_REUSEADDR, 再次监听,结果成功

./sock -A -s 10.0.0.17 80

有趣的是,上面这个例子中,如果监听的地址是0.0.0.0, 则必须在第一步使用SO_REUSEADDR, 否则会导致第五步./sock -A -s 0.0.0.0 80失败了。

  1. client上执行./sock -b 1234 10.0.0.17 80 成功。timewait的五元组被复用了。 这似乎是一个bug。
tcp        0      0 10.0.0.17:8080          10.0.0.17:1234          TIME_WAIT   -

该timewait的五元组被新的连接复用了。

关于SO_REUSEADDR 其他的行为

在BSD系统中,该option的本意是如果有bind 0.0.0.0:port1的socket, 则其他socket bind 一个具体的local ip:port1可以被允许。但是在linux中,这种行为不被允许。
也就是说wilcard address 和具体的local ip之间有互斥关系。
故这个option在不同的os之间是不兼容的.

实验一:

  1. ./sock -A -s 0.0.0.0 8080

  2. ./sock -A -s 10.0.0.17 8080

结果
can’t bind local address: Address already in use

实验二:

将实验一的步骤反过来

  1. ./sock -A -s 10.0.0.17 8080
  2. ./sock -A -s 0.0.0.0 8080

can’t bind local address: Address already in use

net.ipv4.tcp_tw_reuse

从上文中,我们知道通过设置socket option SO_REUSEADDR, 可以bind 并监听time wait状态的address,这对快速重启server 有很大的帮助,不过对于connect() 复用该地址没有什么作用。这是因为tcp_connect()实现中,对于分配到的port的检查和bind() 不完全一样。为了让connect过程可以快速复用tw port,可以设置内核参数net.ipv4.tcp_tw_reuse.

By enabling net.ipv4.tcp_tw_reuse, Linux will reuse an existing connection in the TIME-WAIT state for a new outgoing connection if the new timestamp is strictly bigger than the most recent timestamp recorded for the previous connection: an outgoing connection in the TIME-WAIT state can be reused after just one second.

更详细的解释请参考
https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux

实验步骤:

  1. client上

sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.tcp_timestamps=1

  1. server上启动server

./sock -s 0.0.0.0 8080

  1. client上模拟connect耗尽临时端口,使得这些端口都变成timewati状态的。
    再次发起connect,它会复用timewait的port

为了使得测试容易,缩小临时端口的数量。

sysctl -w net.ipv4.ip_local_port_range=“32768 32769”

在client上

a) ./sock 192.168.60.131 8080

b) 退出sock, 32768 port进入tw。

tcp        0      0 192.168.60.133:32768    192.168.60.131:8080     TIME_WAIT   -

c) 再执行 ./sock 192.168.60.131 8080 发现32768 复用了。

tcp        0      0 192.168.60.133:32768    192.168.60.131:8080     ESTABLISHED 3618/./sock

通过bpftrace在内核关键函数加探针,证现tcp_tw_reuse的判断机制发挥了作用。

bpftrace -e 'kretprobe:tcp_twsk_unique{ printf("return %d",retval); }'
Attaching 1 probe...
^Creturn 1

TCP_TW_RECYCLE

该内核参数在4.12以后废弃了。原因是它不仅仅影响了出的报文,还影响了进的报文。如果进的报文是从nat 设备来的,由于不同的host的timestamp 不一致,会导致丢包。

这篇关于Linux tcp timewait相处之道的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1141535

相关文章

Linux之计划任务和调度命令at/cron详解

《Linux之计划任务和调度命令at/cron详解》:本文主要介绍Linux之计划任务和调度命令at/cron的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux计划任务和调度命令at/cron一、计划任务二、命令{at}介绍三、命令语法及功能 :at

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

kali linux 无法登录root的问题及解决方法

《kalilinux无法登录root的问题及解决方法》:本文主要介绍kalilinux无法登录root的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,... 目录kali linux 无法登录root1、问题描述1.1、本地登录root1.2、ssh远程登录root2、

Linux ls命令操作详解

《Linuxls命令操作详解》通过ls命令,我们可以查看指定目录下的文件和子目录,并结合不同的选项获取详细的文件信息,如权限、大小、修改时间等,:本文主要介绍Linuxls命令详解,需要的朋友可... 目录1. 命令简介2. 命令的基本语法和用法2.1 语法格式2.2 使用示例2.2.1 列出当前目录下的文

Linux中的计划任务(crontab)使用方式

《Linux中的计划任务(crontab)使用方式》:本文主要介绍Linux中的计划任务(crontab)使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、前言1、linux的起源与发展2、什么是计划任务(crontab)二、crontab基础1、cro

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

Linux系统中卸载与安装JDK的详细教程

《Linux系统中卸载与安装JDK的详细教程》本文详细介绍了如何在Linux系统中通过Xshell和Xftp工具连接与传输文件,然后进行JDK的安装与卸载,安装步骤包括连接Linux、传输JDK安装包... 目录1、卸载1.1 linux删除自带的JDK1.2 Linux上卸载自己安装的JDK2、安装2.1

Linux卸载自带jdk并安装新jdk版本的图文教程

《Linux卸载自带jdk并安装新jdk版本的图文教程》在Linux系统中,有时需要卸载预装的OpenJDK并安装特定版本的JDK,例如JDK1.8,所以本文给大家详细介绍了Linux卸载自带jdk并... 目录Ⅰ、卸载自带jdkⅡ、安装新版jdkⅠ、卸载自带jdk1、输入命令查看旧jdkrpm -qa