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,一经查实,立即删除!

相关文章

纠错码较副本优势与minio纠错码配置

1. 优势 纠错码是什么,怎么保障数据不丢失 纠错码采用数据块校验块方式进行存储,副本采用真实副本存储纠错码更利于存储,副本更利于计算 2. 业界配置 EMC对象存储系统ECS 12 4 和 102 冗余度分别为 1.33 、 1.2 阿里云盘古集群chunk存储 83 冗余度1.375 Google RS(6,3) i…

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表 …

java mysql读取多条数据_myeclipse 使用Java访问mysql数据库,数据库中有多条记录,为何只能读出一条数据??...

Java源代码如下&#xff1a;publicListgetAllMsgList(){ListmsgListnewArrayList();DBConnectiondbnewDBConnection();Connectionconndb.getConn();Stringsql"select*frommsg...Java 源代码如下&#xff1a;public List getAllMsgList() { List msgList new ArrayList();…

基于corundumstudio建立websocket长连接

依赖 <!--socket io --><dependency><groupId>io.socket</groupId><artifactId>socket.io-client</artifactId><version>1.0.1</version></dependency><dependency><groupId>com.corundumstudio.socketio&…

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相邻的格子

mysql增加布尔字段_JDBC对MySQL数据库布尔字段的操作方法

本文实例讲述了JDBC对MySQL数据库布尔字段的操作方法。分享给大家供大家参考。具体分析如下&#xff1a;在Mysql数据库如果要使用布尔字段&#xff0c;而应该设置为BIT(1)类型此类型在Mysql中不能通过MySQLQueryBrowser下方的Edit与Apply Changed去编辑只能通过语句修改&#x…

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 不同服务器不同库之间的访问_不同服务器数据库之间的数据操作

--创建链接服务器exec sp_addlinkedserver ITSV , , SQLOLEDB , 远程服务器名或ip地址 exec sp_addlinkedsrvlogin ITSV , false ,null, 用户名 , 密码 --查询示例select * from ITSV.数据库名.dbo.表名--导入示例select * into 表 from ITSV.数据库名.dbo.表名--以后不再使用…

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…

mysql 设置 character_set_server_MySQL:简单记录character_set_server影响参数

Waiting for global read lock&#xff1a;由于flush table with read lock调用函数lock_global_read_lock导致DML操作堵塞。Waiting for commit lock &#xff1a;由于flush table with read lock 调用函数make_global_read_lock_block_commit导致事务不能提交现象堵塞COMMIT和…

mysql3.5.2 下载_mybatis 3.5.2 jar 下载

本文更新日期&#xff1a;2019年9月21日很多人找不到mybatis jar或者下载mybatis jar需要付积分&#xff0c;所以本页面给大家提供一个便捷的下载通道&#xff0c;敬请关注。一、mybatis 3.5.2版本下载&#xff1a;此压缩包包含文件&#xff1a;(1)mybatis-3.5.2.jar(2)mybatis…

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 数据仓库 元数据_数据仓库中的元数据管理

1. 引言元数据是数据仓库中的一个重要组成部分&#xff0c;元数据管理系统则是构建&#xff0c;管理&#xff0c;维护和使用数据仓库系统的核心部件。2. 基础知识2.1 元数据的定义元数据是指来自企业内外的所有物理数据和知识&#xff0c;包括物理数据的格式&#xff0c;技术和…

python列表有固定大小吗_如何在python中创建固定大小的列表?

(tl&#xff1b;dr&#xff1a;对您的问题的确切答案是numpy.empty或numpy.empty_like&#xff0c;但是您可能不在乎&#xff0c;可以使用myList [None]*10000。)简单方法您可以将列表初始化为所有相同的元素。使用一个非数字值(如果以后使用它会给出一个错误&#xff0c;这是…

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…

mysql将时间戳转化为天数_mysql 将时间戳直接转换成日期时间

我的应用&#xff1a;select *,FROM_UNIXTIME(create_at, %Y-%m-%d) as date from stock转载原文&#xff1a;FROM_UNIXTIME( ):转为时间戳类型时间UNIX_TIMESTAMP( ) :返回长类型时间from_unixtime()是MySQL里的时间函数select uid,userid,username,email,FROM_UNIXTIME(addti…