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

相关文章

新特性抢先看! Ubuntu 25.04 Beta 发布:Linux 6.14 内核

《新特性抢先看!Ubuntu25.04Beta发布:Linux6.14内核》Canonical公司近日发布了Ubuntu25.04Beta版,这一版本被赋予了一个活泼的代号——“Plu... Canonical 昨日(3 月 27 日)放出了 Beta 版 Ubuntu 25.04 系统镜像,代号“Pluc

Linux安装MySQL的教程

《Linux安装MySQL的教程》:本文主要介绍Linux安装MySQL的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux安装mysql1.Mysql官网2.我的存放路径3.解压mysql文件到当前目录4.重命名一下5.创建mysql用户组和用户并修

Linux上设置Ollama服务配置(常用环境变量)

《Linux上设置Ollama服务配置(常用环境变量)》本文主要介绍了Linux上设置Ollama服务配置(常用环境变量),Ollama提供了多种环境变量供配置,如调试模式、模型目录等,下面就来介绍一... 目录在 linux 上设置环境变量配置 OllamPOgxSRJfa手动安装安装特定版本查看日志在

Linux系统之主机网络配置方式

《Linux系统之主机网络配置方式》:本文主要介绍Linux系统之主机网络配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、查看主机的网络参数1、查看主机名2、查看IP地址3、查看网关4、查看DNS二、配置网卡1、修改网卡配置文件2、nmcli工具【通用

Linux系统之dns域名解析全过程

《Linux系统之dns域名解析全过程》:本文主要介绍Linux系统之dns域名解析全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、dns域名解析介绍1、DNS核心概念1.1 区域 zone1.2 记录 record二、DNS服务的配置1、正向解析的配置

Linux修改pip和conda缓存路径的几种方法

《Linux修改pip和conda缓存路径的几种方法》在Python生态中,pip和conda是两种常见的软件包管理工具,它们在安装、更新和卸载软件包时都会使用缓存来提高效率,适当地修改它们的缓存路径... 目录一、pip 和 conda 的缓存机制1. pip 的缓存机制默认缓存路径2. conda 的缓

Linux修改pip临时目录方法的详解

《Linux修改pip临时目录方法的详解》在Linux系统中,pip在安装Python包时会使用临时目录(TMPDIR),但默认的临时目录可能会受到存储空间不足或权限问题的影响,所以本文将详细介绍如何... 目录引言一、为什么要修改 pip 的临时目录?1. 解决存储空间不足的问题2. 解决权限问题3. 提

Linux中的进程间通信之匿名管道解读

《Linux中的进程间通信之匿名管道解读》:本文主要介绍Linux中的进程间通信之匿名管道解读,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、基本概念二、管道1、温故知新2、实现方式3、匿名管道(一)管道中的四种情况(二)管道的特性总结一、基本概念我们知道多

Linux中的缓冲区和文件系统详解

《Linux中的缓冲区和文件系统详解》:本文主要介绍Linux中的缓冲区和文件系统方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、FILE结构1、fd2、缓冲区二、文件系统1、固态硬盘2、逻辑地址LBA(一)数据块 Data blocks(二)inode表

Linux系统中配置静态IP地址的详细步骤

《Linux系统中配置静态IP地址的详细步骤》本文详细介绍了在Linux系统中配置静态IP地址的五个步骤,包括打开终端、编辑网络配置文件、配置IP地址、保存并重启网络服务,这对于系统管理员和新手都极具... 目录步骤一:打开终端步骤二:编辑网络配置文件步骤三:配置静态IP地址步骤四:保存并关闭文件步骤五:重