golang 网络编程之如何正确关闭tcp连接以及管理它的生命周期
欢迎访问我的个人网站获取更佳阅读排版 golang 网络编程之如何正确关闭tcp连接以及管理它的生命周期 | yoko blog (https://pengrl.com/p/47401/)
本篇文章部分内容涉及到tcp协议以及socket编程的通用底层知识。讨论的tcp连接对象皆为golang的net.conn对象。如果存在错误,请一定指正,谢谢。
先上结论
- Read方法返回EOF错误,表示本端感知到对端已经关闭连接(本端已接收到对端发送的FIN)。此后如果本端不调用Close方法,只释放本端的连接对象,则连接处于非完全关闭状态(CLOSE_WAIT)。即文件描述符发生泄漏。
- Write方法返回broken pipe错误,表示本端感知到对端已经关闭连接(本端已接收到对端发送的RST)。此后本端可不调用Close方法。连接处于完全关闭状态。
- 由于golang里net.conn内部对文件描述符的所有io操作都有状态保护,所以即使在对端或本端关闭了连接之后,依然可以任意次数调用Read、Write、Close方法。
个人认为正确、简单、语义清晰、高效的做法:应该在Read或Write返回错误后调用Close。不论是主动关闭还是被动关闭,调用Close后,不应该再Read或Write,并尽快释放net.conn对象。
部分demo测试与分析
我的测试环境: go version go1.11.4 darwin/amd64
第三方工具: netstat和wireshark
验证结论一
假设我们有两个demo程序——server和client。
client主动连接上server后不做任何操作,直接关闭net.conn对象。用于模拟主动关闭端。伪代码如下:
conn, _ := net.Dial("tcp", "127.0.0.1:8081")
conn.Close()
server在accept新连接后,在新连接的处理函数中调用Read方法,Read返回io.EOF后不调用Close方法,直接退出处理函数,释放连接对象。伪代码如下:
func handleConn(conn net.Conn) {
buf := make([]byte, 1024)
n, err := conn.Read(buf)
log.Println(n, err)
//conn.Close()
}
启动server后,再启动client,server打印出0 EOF。
用netstat查看连接情况:
# 在shell中输入
$netstat -an | grep 8081
tcp4 0 0 127.0.0.1.8081 127.0.0.1.62871 CLOSE_WAIT
tcp4 0 0 127.0.0.1.62871 127.0.0.1.8081 FIN_WAIT_2
tcp46 0 0 *.8081 *.* LISTEN
client处于FIN_WAIT_2状态,说明client发送了FIN,并收到了对应的ACK。
server处于CLOSE_WAIT状态,说明server收到了FIN,并发送了对应的ACK。
用wireshark抓包:
再测试一遍,发现client发送了FIN,server回复了对应的ACK。但是server并没有发送FIN。与netstat显示的状态相符合。
修改server代码,在Read返回EOF后,调用conn.Close()
重新测试,再使用netstat和wireshark分析,发现server也发送了FIN,两端都正常关闭。
验证结论二
修改server代码。伪代码如下:
buf := make([]byte, 1024)
time.Sleep(5 * time.Second)
n, err := conn.Write(buf)
log.Println(n, err)
time.Sleep(5 * time.Second)
n, err = conn.Write(buf)
log.Println(n, err)
server输出如下:
2019/04/17 16:08:17 1024 <nil>
2019/04/17 16:08:18 0 write tcp 127.0.0.1:8081->127.0.0.1:64124: write: broken pipe
server的第一次Sleep 5秒是为了确保在第一次Write之前client已关闭连接。
用netstat观察:
我们发现在5秒内,server处于CLOSE_WAIT状态,client处于FIN_WAIT_2状态。
5秒之后,两端都进入完全关闭状态。
用wireshark抓包:
发现5秒后,server向client发送第一次1024字节数据后,client向server回复了RST包。
10秒后,server并不会再发送第二次的1024字节数据。
server的第二次Sleep 5秒是为了确保在第一次Write之后,server接收到了RST包。如果去掉第二次的Sleep,可能出现server连续发送两次数据给client,client回复两次RST给server。
验证结论三
场景一
对端关闭后,本端一直Read,则一直得到EOF错误。
这是由于系统调用Read会一直返回0。
场景二
对端关闭后,本端一直Write,则一直得到如下错误:
write tcp 127.0.0.1:8081->127.0.0.1:63520: write: broken pipe
这是由于系统调用Write会一直返回EPIPE。
场景三
本端关闭后,本端继续调用Read或Write或Close,则一直得到如下错误:
127.0.0.1:63482->127.0.0.1:8081: use of closed network connection
127.0.0.1:63448->127.0.0.1:8081: use of closed network connection
这是由fd_mutex.go中的mutexClosed标志决定的,当文件描述符被关闭后,该标志会被设置,之后所有io操作都会返回错误。
golang 网络编程之如何正确关闭tcp连接以及管理它的生命周期的更多相关文章
- 网络编程的基本概念,TCP/IP协议简介
8.1.1 网络基础知识 计算机网络形式多样,内容繁杂.网络上的计算机要互相通信,必须遵循一定的协议.目前使用最广泛的网络协议是Internet上所使用的TCP/IP协议. 网络编程的目的就是指直接或 ...
- HTTP的RST包与WinHttp延迟关闭TCP连接
一.RST包也常见于断开TCP连接 几个月前用wireshark抓HTTP包发现有的网络通信在结束的时候没有使用四次握手,而是直接使用RST包.如: 在TCP协议中RST表示复位,用来异常的关闭连接 ...
- (1)线程的同步机制 (2)网络编程的常识 (3)基于tcp协议的编程模型
1.线程的同步机制(重点)1.1 基本概念 当多个线程同时访问同一种共享资源时可能会造成数据的覆盖和不一致等问题,此时就需要对线程之间进行协调和通信,该方式就叫线程的同步机制. 如: 2003年左右 ...
- 网络编程 套接字socket TCP UDP
网络编程与套接字 网络编程 网络编程是什么: 网络通常指的是计算机中的互联网,是由多台计算机通过网线或其他媒介相互链接组成的 编写基于网络的应用程序的过程序称之为网络编程. 网络编程最主要的工 ...
- 【UNIX网络编程(三)】TCP客户/server程序演示样例
上一节给出了TCP网络编程的函数.这一节使用那些基本函数编写一个完毕的TCP客户/server程序演示样例. 该样例运行的过程例如以下: 1.客户从标准输入读入一行文本,并写给server. 2.se ...
- (1)网络编程的常识 (2)基于tcp协议的编程模型 (3)tcp协议和udp协议的比较 (4)基于udp协议的编程模型
1.网络编程的常识 目前主流的网络通讯软件有:微信.QQ.YY.陌陌.探探.飞信.阿里旺旺.... 在吗? 1.1 七层网络模型(熟悉) 为了保证数据传递的可靠安全等等,ISO(国际标准委员会组织)将 ...
- 网络编程----socket介绍、基于tcp协议的套接字实现、基于udp协议的套接字实现
一.客户端/服务器架构(C/S架构) 即C/S架构,包括: 1.硬件C/S架构(打印机) 2.软件C/S架 ...
- 【TCP/IP网络编程】:04基于TCP的服务器端/客户端
摘要:结合前面所讲述的知识,本篇文章主要介绍了简单服务器端和客户端实现的框架流程及相关函数接口. 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字(本 ...
- python网络编程socketserver模块(实现TCP客户端/服务器)
摘录python核心编程 socketserver(python3.x版本重新命名)是标准库中的网络编程的高级模块.通过将创建网络客户端和服务器所必须的代码封装起来,简化了模板,为你提供了各种各样的类 ...
随机推荐
- 3495: PA2010 Riddle 2-sat 前缀优化
3495: PA2010 Riddle 2-sat 前缀优化 链接 bzoj 思路 不想说啥了,看hwim的吧,我去睡觉了zZ. 代码 /******************************* ...
- 洛谷P2949题解
若想要深入学习反悔贪心,传送门. Description: 有 \(n\) 项工作,每 \(i\) 项工作有一个截止时间 \(D_i\) ,完成每项工作可以得到利润 \(P_i\) ,求最大可以得到多 ...
- HTML引入外部JS文件
<!--引入外部文件的方式--> <script type="text/javascript" src="attack.js">< ...
- java判断指定路径文件夹是否存在,若不存在则创建新的文件夹
File file = new File(dirPath); if (!file.exists()) { file.mkdirs(); }
- session与getSession()用法总结
一.session 1.session的过期时间是从什么时候开始计算的?是从一登录就开始计算还是说从停止活动开始计算? 从session不活动的时候开始计算,如果session一直活动,session ...
- linux安装nvm和nodejs
下载nvm包: [root@centos ~]# wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install ...
- invalid argument (errno: 22)
socket通信示例中,当accept客户端时,经常报这个错误. 并且是第一次没有问题,第二次或后面几次都会出现如下问题, 错误码为22, 错误描述为invalid argument. 问题解决如下: ...
- 初识RxSwift
初识Observable Observable<T> 这个类就是Rx框架的基础, 我们可以称他为可观察序列, 他的作用就是可以异步的产生一系列的Event(事件), 即一个Observab ...
- 运行应用-使用Deployment运行无状态应用程序
以下介绍如何使用kubernetes的deployment对象运行应用程序. 目标 - 创建nginx deployment. - 使用kubectl列出有关部署的信息. - 更新部署 创建和探索ng ...
- linux默认的2.7升级到3.7版本
CentOS7中自带的python版本是python-2.7.5,由于新开的虚拟机需要使用python3,于是便升级一下版本. 安装Python3.7.3 官网下载地址:https://www.pyt ...