- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Go 中如何强制关闭 TCP 连接由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
在《Go 网络编程和 TCP 抓包实操》一文中,我们编写了 Go 版本的 TCP 服务器与客户端代码,并通过 tcpdump 工具进行抓包获取分析。在该例中,客户端代码通过调用 Conn.Close() 方法发起了关闭 TCP 连接的请求,这是一种默认的关闭连接方式.
默认关闭需要四次挥手的确认过程,这是一种”商量“的方式,而 TCP 为我们提供了另外一种”强制“的关闭模式.
如何强制性关闭?具体在 Go 代码中应当怎样实现?这就是本文探讨的内容.
相信每个程序员都知道 TCP 断开连接的四次挥手过程,这是面试八股文中的股中股。我们在 Go 代码中调用默认的 Conn.Close() 方法,它就是典型的四次挥手.
以客户端主动关闭连接为例,当它调用 Close 函数后,就会向服务端发送 FIN 报文,如果服务器的本端 socket 接收缓存区里已经没有数据,那服务端的 read 将会得到一个 EOF 错误.
发起关闭方会经历 FIN_WAIT_1 -> FIN_WAIT_2 -> TIME_WAIT -> CLOSE 的状态变化,这些状态需要得到被关闭方的反馈而更新.
默认的关闭方式,不管是客户端还是服务端主动发起关闭,都要经过对方的应答,才能最终实现真正的关闭连接。那能不能在发起关闭时,不关心对方是否同意,就结束掉连接呢?
答案是肯定的。TCP 协议为我们提供了一个 RST 的标志位,当连接的一方认为该连接异常时,可以通过发送 RST 包并立即关闭该连接,而不用等待被关闭方的 ACK 确认.
SetLinger() 方法 。
在 Go 中,我们可以通过 net.TCPConn.SetLinger() 方法来实现.
函数的注释已经非常清晰,但是需要读者有 socket 缓冲区的概念.
当应用层代码通过 socket 进行读与写的操作时,实质上经过了一层 socket 缓冲区,它分为发送缓冲区和接受缓冲区.
缓冲区信息可通过执行 netstat -nt 命令查看 。
其中,Recv-Q 代表的就是接收缓冲区,Send-Q 代表的是发送缓冲区.
默认关闭方式中,即 sec < 0 。操作系统会将缓冲区里未处理完的数据都完成处理,再关闭掉连接.
当 sec > 0 时,操作系统会以与默认关闭方式运行。但是当超过定义的时间 sec 后,如果还没处理完缓存区的数据,在某些操作系统下,缓冲区中未完成的流量可能就会被丢弃.
而 sec == 0 时,操作系统会直接丢弃掉缓冲区里的流量数据,这就是强制性关闭.
我们通过示例代码来学习 SetLinger() 的使用,并以此来分析强制关闭的区别.
服务端代码 。
以服务端为主动关闭连接方示例 。
服务端代码根据逻辑分为四个部分 。
第一部分:端口监听。我们通过 net.Listen("tcp", ":8000")开启在端口 8000 的 TCP 连接监听.
第二部分:建立连接。在开启监听成功之后,调用 net.Listener.Accept()方法等待 TCP 连接。Accept 方法将以阻塞式地等待新的连接到达,并将该连接作为 net.Conn 接口类型返回.
第三部分:数据传输。当连接建立成功后,我们将启动一个新的 goroutine 来处理 c 连接上的读取和写入。本文服务器的数据处理逻辑是,客户端写入该 TCP 连接的所有内容,服务器将原封不动地写回相同的内容.
第四部分:强制关闭连接逻辑。启动一个新的 goroutine,通过 c.(*net.TCPConn).SetLinger(0) 设置强制关闭选项,并于 10 s 后关闭连接.
客户端代码 。
以客户端为被动关闭连接方示例 。
客户端代码根据逻辑分为三个部分 。
第一部分:建立连接。我们通过 net.Dial("tcp", "localhost:8000")连接一个 TCP 连接到服务器正在监听的同一个 localhost:8000 地址.
第二部分:写入数据。当连接建立成功后,通过 c.Write() 方法写入数据 Hi, gopher? 给服务器.
第三部分:读取数据。除非发生 error,否则客户端通过 c.Read() 方法(记住,是阻塞式的)循环读取 TCP 连接上的内容.
tcpdump 抓包结果 。
tcpdump 是一个非常好用的数据抓包工具,在《Go 网络编程和 TCP 抓包实操》一文中已经简单介绍了它的命令选项,这里就不再赘述.
服务器和客户端建立连接之后,从客户端读取到数据 Hi, gopher? 。在 10s 后,服务端强制关闭了 TCP 连接,阻塞在 c.Read 的服务端代码返回了错误: use of closed network connection.
客户端和服务器建立连接之后,发送数据给服务端,服务端返回相同的数据 Hi, gopher? 回来。在 10s 后,由于服务器强制关闭了 TCP 连接,因此阻塞在 c.Read 的客户端代码捕获到了错误:connection reset by peer.
我们重点关注内容 Flags [],其中 [S] 代表 SYN 包,用于建立连接;[P] 代表 PSH 包,表示有数据传输;[R]代表 RST 包,用于重置连接;[.] 代表对应的 ACK 包。例如 [S.] 代表 SYN-ACK.
搞懂了这几个 Flags 的含义,那我们就可以分析出本次服务端强制关闭的 TCP 通信全过程.
我们和《Go 网络编程和 TCP 抓包实操》一文中,客户端正常关闭的通信过程进行比较 。
可以看到,当通过设定 SetLinger(0) 之后,主动关闭方调用 Close() 时,系统内核会直接发送 RST 包给被动关闭方。这个过程并不需要被动关闭方的回复,就已关闭了连接。主动关闭方也就没有了默认关闭模式下 FIN_WAIT_1 -> FIN_WAIT_2 -> TIME_WAIT -> CLOSE 的状态改变.
本文我们介绍了 TCP 默认关闭与强制关闭两种方式(其实还有种折中的方式:SetLinger(sec > 0)),它们都源于 TCP 的协议设计.
在大多数的场景中,我们都应该选择使用默认关闭方式,因为这样才能确保数据的完整性(不会丢失 socket 缓冲区里的数据).
当使用默认方式关闭时,每个连接都会经历一系列的连接状态转变,让其在操作系统上停留一段时间。尤其是服务器要主动关闭连接时(大多数应用场景,都应该是由客户端主动发起关闭操作),这会消耗服务器的资源.
如果短时间内有大量的或者恶意的连接涌入,我们或许需要采用强制关闭方式。因为使用强制关闭,能立即关闭这些连接,释放资源,保证服务器的可用与性能.
当然,我们还可以选择折中的方式,容忍一段时间的缓存区数据处理时间,再进行关闭操作.
这里给读者朋友留一个思考题。如果在本文示例中,我们将 SetLinger(0) 改为 SetLinger(1) ,抓包结果又会是如何?
最后,读者朋友们在项目中,有使用过强制关闭方式吗?
原文链接:https://mp.weixin.qq.com/s/Lbv4myGcvH6fIfNoQfyqQA 。
最后此篇关于Go 中如何强制关闭 TCP 连接的文章就讲到这里了,如果你想了解更多关于Go 中如何强制关闭 TCP 连接的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在使用 go 图表库 https://github.com/wcharczuk/go-chart制作条形图。我面临的问题是标签值很长,我想将文本旋转 45 度以显示完整文本 我喜欢显示的日期格式是
我在构建一个非常简单的通过 cgo 调用 c 代码的 go 程序时遇到了问题。我的设置: $: echo $GOPATH /go $: pwd /go/src/main $: ls ctest.c
没有 C 的背景,只有 Go 的“初学者”经验,我正在尝试弄清楚 main.go 是实际需要的还是只是一个约定。 我想创建一个简单的网络 API,但有人可以为我澄清一下吗? 最佳答案 main.go
我read从 Go 1.4 开始,Go 运行时是用 Go 本身编写的(而不是用 C)。 这怎么可能?如果 Go 程序在运行时之上运行,并且运行时是 Go 程序,那么运行时是否在自身之上运行? 最佳答案
这是“Go 之旅”中的代码示例 Range and Close : package main import ( "fmt" ) func fibonacci(n int, c chan int
给定以下 go.mod 文件: module foo go 1.12 require ( github.com/bar/baz v1.0.0 github.com/rat/cat v1
我有一个 CI/CD 管道,它需要跨平台并与几个不同的管理程序一起工作。为了不必更改 Windows 和 Linux 的构建任务,我认为 Go 将是编写一次代码并在任何地方运行的好方法。然而,考虑到
我有一个 Dockerfile,用于使用 go build 编译 Go 应用程序。我进行了研究,确实建议将 go build 用于生产。 但是我找不到正确的答案来解释为什么。 我了解 go run 创
我尝试在命令提示符#Go lang 中运行该程序-但是当我键入运行“go run hello.go”命令时,我开始了 CreateFile hello.go:The system cannot fin
我正在使用“Go 编程语言”一书学习 Go。第一章介绍os.Open用于读取文件的模块。我尝试打开如下所示的 go 文件。 f, err = os.Open("helloworld.go") 我收
关闭。这个问题需要details or clarity .它目前不接受答案。 想改进这个问题?通过 editing this post 添加详细信息并澄清问题. 2年前关闭。 Improve this
为了解决我对 goroutine 的一些误解,我去了 Go 操场跑了 this code : package main import ( "fmt" ) func other(done cha
这个问题在这里已经有了答案: Evaluate/Execute Golang code/expressions like js' eval() (5 个回答) 1年前关闭。 对于任何 go 程序,我想
这是我基本上试图从路径打印基准的代码。 这意味着,如果用户输入“/some/random/path.java”,则输出将为“path”。同样,如果用户arg为“/another/myapp.c”,则输
$ go version 1.13.3 我的文件夹结构如下: GOPATH +---src +--- my-api-server +--- my-auth-server
这个问题在这里已经有了答案: How to embed file for later parsing execution use (4 个答案) What's the best way to bun
我觉得这有点奇怪,为什么这段代码不起作用? package main import "fmt" func main() { var i, j int = 1, 2 k
go编译器执行完如下命令后的可执行文件存放在哪里? $> go run file.go 最佳答案 在 /tmp 文件夹中,如果您使用的是 unix 机器。 如果您使用的是 Windows,则在 \Us
我目前正在开始使用 Go,并且已经深入研究了有关包命名和工作区文件夹结构的注意事项。 不过,我不太确定如何根据 Go 范式正确组织我的代码。 这是我当前的结构示例,它位于 $GOPATH/src 中:
假设我有一个接受用户输入的 Lua 程序,而该输入恰好是有效的 Lua 源代码。这是在程序仍在运行时进行清理、编译和执行的。 Go 是否(或将)实现这样的事情? 最佳答案 我认为以下两个项目之间有足够
我是一名优秀的程序员,十分优秀!