go tcp连接_TCP漫谈之keepalive和time_wait

TCP是一个有状态通讯协议,所谓的有状态是指通信过程中通信的双方各自维护连接的状态。

一、TCP keepalive

先简单回顾一下TCP连接建立和断开的整个过程。(这里主要考虑主流程,关于丢包、拥塞、窗口、失败重试等情况后面详细讨论。)

首先是客户端发送syn(Synchronize Sequence Numbers:同步序列编号)包给服务端,告诉服务端我要连接你,syn包里面主要携带了客户端的seq序列号;服务端回发一个syn+ack,其中syn包和客户端原理类似,只不过携带的是服务端的seq序列号,ack包则是确认客户端允许连接;最后客户端再次发送一个ack确认接收到服务端的syn包。这样客户端和服务端就可以建立连接了。整个流程称为三次握手。

a1bdf947d375e8689c613ddb41e807e8.png

建立连接后,客户端或者服务端便可以通过已建立的socket连接发送数据,对端接收数据后,便可以通过ack确认已经收到数据。

数据交换完毕后,通常是客户端便可以发送FIN包,告诉另一端我要断开了;另一端先通过ack确认收到FIN包,然后发送FIN包告诉客户端我也关闭了;最后客户端回应ack确认连接终止。整个流程成为四次挥手。

TCP的性能经常为大家所诟病,除了TCP+IP额外的header以外,它建立连接需要三次握手,关闭连接需要四次挥手。如果只是发送很少的数据,那么传输的有效数据是非常少的。

是不是建立一次连接后续可以继续复用呢?的确可以这样做,但这又带来另一个问题,如果连接一直不释放,端口被占满了咋办。为此引入了今天讨论的第一个话题TCP keepalive。所谓的TCP keepalive是指TCP连接建立后会通过keepalive的方式一直保持,不会在数据传输完成后立刻中断,而是通过keepalive机制检测连接状态。

Linux控制keepalive有三个参数:保活时间net.ipv4.tcp_keepalive_time、保活时间间隔net.ipv4.tcp_keepalive_intvl、保活探测次数net.ipv4.tcp_keepalive_probes,默认值分别是 7200 秒(2 小时)、75 秒和 9 次探测。如果使用 TCP 自身的 keep-Alive 机制,在 Linux 系统中,最少需要经过 2 小时 + 9*75 秒后断开。譬如我们SSH登录一台服务器后可以看到这个TCP的keepalive时间是2个小时,并且会在2个小时后发送探测包,确认对端是否处于连接状态。

78501d8b0ebbb086a73abfaafffe5e2c.png

之所以会讨论TCP的keepalive,是因为发现服器上有泄露的TCP连接:

# ll /proc/11516/fd/10lrwx------ 1 root root 64 Jan 3 19:04 /proc/11516/fd/10 -> socket:[1241854730]# dateSun Jan 5 17:39:51 CST 2020

已经建立连接两天,但是对方已经断开了(非正常断开)。由于使用了比较老的go(1.9之前版本有问题)导致连接没有释放。

解决这类问题,可以借助TCP的keepalive机制。新版go语言支持在建立连接的时候设置keepalive时间。首先查看网络包中建立TCP连接的DialContext方法中

if tc, ok := c.(*TCPConn); ok && d.KeepAlive >= 0 {setKeepAlive(tc.fd, true)ka := d.KeepAliveif d.KeepAlive == 0 {ka = defaultTCPKeepAlive}setKeepAlivePeriod(tc.fd, ka)testHookSetKeepAlive(ka)}

其中defaultTCPKeepAlive是15s。如果是HTTP连接,使用默认client,那么它会将keepalive时间设置成30s。

var DefaultTransport RoundTripper = &Transport{Proxy: ProxyFromEnvironment,DialContext: (&net.Dialer{Timeout: 30 * time.Second,KeepAlive: 30 * time.Second,DualStack: true,}).DialContext,ForceAttemptHTTP2: true,MaxIdleConns: 100,IdleConnTimeout: 90 * time.Second,TLSHandshakeTimeout: 10 * time.Second,ExpectContinueTimeout: 1 * time.Second,}

下面通过一个简单的demo测试一下,代码如下:

func main() {wg := &sync.WaitGroup{}c := http.DefaultClientfor i := 0; i < 2; i++ {wg.Add(1)go func() {defer wg.Done()for {r, err := c.Get("http://10.143.135.95:8080")if err != nil {fmt.Println(err)return}_, err = ioutil.ReadAll(r.Body)r.Body.Close()if err != nil {fmt.Println(err)return}time.Sleep(30 * time.Millisecond)}}()}wg.Wait()}

执行程序后,可以查看连接。初始设置keepalive为30s。

94d81affe1cdf6d6bc37114fbfbe46ca.png

然后不断递减,至0后,又会重新获取30s。

95874147bcb5255bd5a8da08aa76011c.png

整个过程可以通过tcpdump抓包获取。

# tcpdump -i bond0 port 35832 -nvv -A

其实很多应用并非是通过TCP的keepalive机制探活的,因为默认的两个多小时检查时间对于很多实时系统是完全没法满足的,通常的做法是通过应用层的定时监测,如PING-PONG机制(就像打乒乓球,一来一回),应用层每隔一段时间发送心跳包,如websocket的ping-pong。

二、TCP Time_wait

第二个希望和大家分享的话题是TCP的Time_wait状态。、

d6836a9eee58eed825581ee6292420d8.png

为啥需要time_wait状态呢?为啥不直接进入closed状态呢?直接进入closed状态能更快地释放资源给新的连接使用了,而不是还需要等待2MSL(Linux默认)时间。

有两个原因:

一是为了防止“迷路的数据包”,如下图所示,如果在第一个连接里第三个数据包由于底层网络故障延迟送达。等待新的连接建立后,这个迟到的数据包才到达,那么将会导致接收数据紊乱。

f553e2098a495149e8ab8bac1efa6e79.png

第二个原因则更加简单,如果因为最后一个ack丢失,那么对方将一直处于last ack状态,如果此时重新发起新的连接,对方将返回RST包拒绝请求,将会导致无法建立新连接。

44f8b40b7b84b5741b2c37eb90f488c0.png

为此设计了time_wait状态。在高并发情况下,如果能将time_wait的TCP复用,time_wait复用是指可以将处于time_wait状态的连接重复利用起来。从time_wait转化为established,继续复用。Linux内核通过net.ipv4.tcp_tw_reuse参数控制是否开启time_wait状态复用。

读者可能很好奇,之前不是说time_wait设计之初是为了解决上面两个问题的吗?如果直接复用不是反而会导致上面两个问题出现吗?这里先介绍Linux默认开启的一个TCP时间戳策略net.ipv4.tcp_timestamps = 1。

00738f5ad63a2f59485568bf0e4f7df0.png

时间戳开启后,针对第一个迷路数据包的问题,由于晚到数据包的时间戳过早会被直接丢弃,不会导致新连接数据包紊乱;针对第二个问题,开启reuse后,当对方处于last-ack状态时,发送syn包会返回FIN,ACK包,然后客户端发送RST让服务端关闭请求,从而客户端可以再次发送syn建立新的连接。

最后还需要提醒读者的是,Linux 4.1内核版本之前除了tcp_tw_reuse以外,还有一个参数tcp_tw_recycle,这个参数就是强制回收time_wait状态的连接,它会导致NAT环境丢包,所以不建议开启。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/508163.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql update 联合更新_Mysql update多表联合更新的方法小结

下面我建两个表&#xff0c;并执行一系列sql语句&#xff0c;仔细观察sql执行后表中数据的变化&#xff0c;很容易就能理解多表联合更新的用法student表 class表1. 执行 UPDATE student s , class c SET s.class_nametest00,c.stu_nametest00 WHERE s.class_id c.idstudent表 …

mysql查询开启事务_MySQL中的查询事务问题

之前帮同学做个app的后台&#xff0c;使用了MySQLMyBatis&#xff0c;遇到了一个查询提交的问题&#xff0c;卡了很久&#xff0c;现在有时间了来复盘下环境情况假设有学生表&#xff1a;USE test;CREATE TABLE student (Id int NOT NULL PRIMARY KEY AUTO_INCREMENT,Name varc…

通过gparted 调整 ubuntu 磁盘

1. 启动和安装 1. 安装 sudo apt-get install gparted 2. 启动 sudo gparted2.配置 如果想扩充磁盘,需要有未分配空间,且该未分配空间位于partition相邻的格子

linux查看mysql表空间使用率_Oracle查看数据库表空间使用情况sql语句

Oracle查看数据库表空间使用情况sql语句SELECT UPPER(F.TABLESPACE_NAME) "表空间名",D.TOT_GROOTTE_MB "表空间大小(M)",D.TOT_GROOTTE_MB - F.TOTAL_BYTES "已使用空间(M)",TO_CHAR(ROUND((D.TOT_GROOTTE_MB - F.TOTAL_…

mysql 禁止转义_必须转义哪些字符才能阻止(我的)SQL注入?

6 个答案:答案 0 :(得分&#xff1a;46)关于退格字符的猜测&#xff1a;想象一下&#xff0c;我发送了一封电子邮件“嗨&#xff0c;这是根据需要更新数据库的查询”和带有的附加文本文件INSERT INTO students VALUES ("Bobby Tables",12,"abc",3.6);你捕获…

mysql 失效转移_MySQL基于MHA的FailOver过程

大家好&#xff0c;我是anyux。本文介绍MySQL基于MHA的FailOver过程。MHA FailOver过程详解什么是FailOver故障转移主库宕机&#xff0c;一直到业务恢复正常的处理过程如何处理FailOver1.快速监控到主库宕机2.选择新主节点&#xff0c;选择策略mysqladmin ping检查数据库状态&a…

kali 切换图形界面_kali Linux 文本图形界面切换遇到的怪问题

前段装了在Virtual Box上装一个Kali Linux玩&#xff0c;然后设为了开机进入文本界面&#xff0c;后来遇到无法上网的问题&#xff0c;网上找到解决方法&#xff0c;说是NAT地址转换和host-only双网卡顺序问题&#xff0c;按照网上的说法调整顺序后一切正常。问题及调整方法详见…

linux mysql更改生效_linux下面MySQL变量修改及生效

今天在访问mysql项目的时候突然报500错误&#xff0c;没有找到连接&#xff0c;因此想到mysql的连接时间。mysql> show global variables;主要就是连接时间是28800(8小时)&#xff0c;而且任务调度也没打开&#xff0c;因此想到修改全局变量的值。1.修改任务调度装:1.1具体的…

linux刻录win10u盘_手把手教你装系统之【制作官方win10安装U盘】

本帖最后由 蚂蚁炒花甲 于 2019-11-11 22:58 编辑很多粉粉在收到linux版本的笔记本后用不惯&#xff0c;但又不知道如何装win10系统下面我就来教大家&#xff0c;如何自己动手 制作win10 安装U盘想学习的粉粉们&#xff0c;可以跟着我 学习下手把手教你装系统之【官方win10 U盘…

mysql 魔术设置_mysql主从复制实践

1.master服务器上安装mysql&#xff0c;正常安装mysql参考2.slave服务器上安装mysql&#xff0c;正常安装mysql参考3.配置3.1master服务器配置cnf文件vim /etc/my.cnf加入配置[mysqld]log-bin master-binlog-bin-index master-bin.indexserver-id 1重启mysql服务service mys…

scrapy爬取天气存MySQL_Scrapy实战篇(五)之爬取历史天气数据

本篇文章我们以抓取历史天气数据为例&#xff0c;简单说明数据抓取的两种方式&#xff1a;1、一般简单或者较小量的数据需求&#xff0c;我们以requests(selenum)beautiful的方式抓取数据2、当我们需要的数据量较多时&#xff0c;建议采用scrapy框架进行数据采集&#xff0c;sc…

mysql 第二天数据_MySQL入门第二天------数据库操作

一、基本命令1、启动服务器cmdnet start [服务器名称]net start mysql572、停止服务器cmdnet stop [服务器名称]net stop mysql573、链接数据库mysql -u 用户名 -p 登录密码mysql -u root -p4、退出登录quitexit\q5、查看版本(连接后执行)select version();6、查看当前时间(连接…

如何打开java_怎样运行java

怎样运行java? 第一步:下载并安装JDK 6.0,安装路劲为: C:\java\jdk1.6 . 第二步:对“我的电脑”按右键,选“属性”,在“系统属性”窗口中选“高级”标签,再按“环境变量”按钮,弹出一个“环境变量”的窗口,在系统变量中新建一个变量,变量名为“JAVA_HOME“,变量值为…

java 字符串匹配_多模字符串匹配算法原理及Java实现代码

多模字符串匹配算法在这里指的是在一个字符串中寻找多个模式字符字串的问题。一般来说&#xff0c;给出一个长字符串和很多短模式字符串&#xff0c;如何最快最省的求出哪些模式字符串出现在长字符串中是我们所要思考的。该算法广泛应用于关键字过滤、入侵检测、病毒检测、分词…

java http 异步请求框架_GitHub - huangdali/MyHttpUtils: 一个非常好用的异步网络请求框架...

轻量级网络请求框架MyHttputils 一、前言本版代码大换血&#xff0c;使用了策略模式和构造模式来组织代码&#xff0c;增加了更加人性化的请求构造&#xff0c;代码质量提高、效率显著提升。(但是使用风格基本没变哦)2.0.2版本的基本的用法在《android网络请求框架》一个轻量级…

mysql 推送微信公众号_10分钟完成微信公众号第三方平台全网发布

背景&#xff1a;在微信公众平台配置服务器URL时&#xff0c;使用了新浪云SAE自带的二级域名&#xff0c;提交时出现一个安全风险的警告&#xff0c;网上查了下&#xff0c;许多服务平台和团队也遇到同样的问题。经过一番研究 …为什么会有安全风险的警告&#xff1f;微信公众平…

java排序算法原理_排序算法原理与实现(java)

排序算法原理与实现(java) Java程序员必知的8大排序 [来源&#xff1a;本站 | 日期&#xff1a;2012年12月24日 | 浏览173 次] 字体:[大 中 小] 8种排序之间的关系: 1&#xff0c; 直接插入排序 (1)基本思想&#xff1a;在要排序的一组数中&#xff0c;假设前面(n-1)[n>2] 个…

ios django 连接mysql_Django---Django连接Mysql数据库

前面介绍了Django平台的数据交互&#xff0c;这些数据都是在本地存放着&#xff0c;修改内容或者重新启动服务&#xff0c;数据就消失了&#xff0c;如果我们把数据存放在数据库中&#xff0c;不就保存了吗&#xff1f;Django数据库Django中自带的也有数据库(sqlite3)&#xff…

java xmpp openfire_XMPP协议学习笔记三(Openfire服务器端搭建开发环境)

在了解了XMPP的基本结构和一些概念之后&#xff0c;我们暂时告别枯燥的理论学习&#xff0c;来动手搭建一下OpenfireSpark的开发环境&#xff0c;实际感受一下搭建整套IM通讯系统的过程。开发环境&#xff1a;windows XP sp3&#xff0c;Eclipse3.6.1&#xff0c;jdk1.6.0_24&a…

java接口经常变动前端怎么办_Java进程故障排查(CPU资源占用高,接口响应超时,功能接口停滞等)...

故障分析# 导致系统不可用情况(频率较大)&#xff1a;1)代码中某个位置读取数据量较大&#xff0c;导致系统内存耗尽&#xff0c;进而出现Full GC次数过多&#xff0c;系统缓慢&#xff1b;2)代码中有比较消耗CPU的操作&#xff0c;导致CPU过高&#xff0c;系统运行缓慢&#x…