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系统: Debian操作系统常用命令指南

《高效管理你的Linux系统:Debian操作系统常用命令指南》在Debian操作系统中,了解和掌握常用命令对于提高工作效率和系统管理至关重要,本文将详细介绍Debian的常用命令,帮助读者更好地使... Debian是一个流行的linux发行版,它以其稳定性、强大的软件包管理和丰富的社区资源而闻名。在使用

Linux Mint Xia 22.1重磅发布: 重要更新一览

《LinuxMintXia22.1重磅发布:重要更新一览》Beta版LinuxMint“Xia”22.1发布,新版本基于Ubuntu24.04,内核版本为Linux6.8,这... linux Mint 22.1「Xia」正式发布啦!这次更新带来了诸多优化和改进,进一步巩固了 Mint 在 Linux 桌面

LinuxMint怎么安装? Linux Mint22下载安装图文教程

《LinuxMint怎么安装?LinuxMint22下载安装图文教程》LinuxMint22发布以后,有很多新功能,很多朋友想要下载并安装,该怎么操作呢?下面我们就来看看详细安装指南... linux Mint 是一款基于 Ubuntu 的流行发行版,凭借其现代、精致、易于使用的特性,深受小伙伴们所喜爱。对

什么是 Linux Mint? 适合初学者体验的桌面操作系统

《什么是LinuxMint?适合初学者体验的桌面操作系统》今天带你全面了解LinuxMint,包括它的历史、功能、版本以及独特亮点,话不多说,马上开始吧... linux Mint 是一款基于 Ubuntu 和 Debian 的知名发行版,它的用户体验非常友好,深受广大 Linux 爱好者和日常用户的青睐,

Linux(Centos7)安装Mysql/Redis/MinIO方式

《Linux(Centos7)安装Mysql/Redis/MinIO方式》文章总结:介绍了如何安装MySQL和Redis,以及如何配置它们为开机自启,还详细讲解了如何安装MinIO,包括配置Syste... 目录安装mysql安装Redis安装MinIO总结安装Mysql安装Redis搜索Red

Linux中Curl参数详解实践应用

《Linux中Curl参数详解实践应用》在现代网络开发和运维工作中,curl命令是一个不可或缺的工具,它是一个利用URL语法在命令行下工作的文件传输工具,支持多种协议,如HTTP、HTTPS、FTP等... 目录引言一、基础请求参数1. -X 或 --request2. -d 或 --data3. -H 或

Linux磁盘分区、格式化和挂载方式

《Linux磁盘分区、格式化和挂载方式》本文详细介绍了Linux系统中磁盘分区、格式化和挂载的基本操作步骤和命令,包括MBR和GPT分区表的区别、fdisk和gdisk命令的使用、常见的文件系统格式以... 目录一、磁盘分区表分类二、fdisk命令创建分区1、交互式的命令2、分区主分区3、创建扩展分区,然后

Linux中chmod权限设置方式

《Linux中chmod权限设置方式》本文介绍了Linux系统中文件和目录权限的设置方法,包括chmod、chown和chgrp命令的使用,以及权限模式和符号模式的详细说明,通过这些命令,用户可以灵活... 目录设置基本权限命令:chmod1、权限介绍2、chmod命令常见用法和示例3、文件权限详解4、ch

Linux内核之内核裁剪详解

《Linux内核之内核裁剪详解》Linux内核裁剪是通过移除不必要的功能和模块,调整配置参数来优化内核,以满足特定需求,裁剪的方法包括使用配置选项、模块化设计和优化配置参数,图形裁剪工具如makeme... 目录简介一、 裁剪的原因二、裁剪的方法三、图形裁剪工具四、操作说明五、make menuconfig

Linux使用nohup命令在后台运行脚本

《Linux使用nohup命令在后台运行脚本》在Linux或类Unix系统中,后台运行脚本是一项非常实用的技能,尤其适用于需要长时间运行的任务或服务,本文我们来看看如何使用nohup命令在后台... 目录nohup 命令简介基本用法输出重定向& 符号的作用后台进程的特点注意事项实际应用场景长时间运行的任务服