女主宣言

最近在团队分享中,关于长链接的服务中,提到了一个TIMEWAIT的解决方案,具体关于TIMEWAIT具体产生的原因,原理,对系统产生的影响,会在其他的文章中给出具体的说明和解释,这个简单介绍一下概括性的内容。

PS:丰富的一线技术、多元化的表现形式,尽在“360云计算”,点关注哦!

简介

  1. 只有断开的发起端才会进入到TIME_WAIT的状态。

  2. TIME_WAIT状态的socket会占用系统的内存和CPU资源,占用的资源相对比较有限,比如内存每个也就是4K(没有具体测试,Net/3实现中,这三个的和位264字节,TCP/IP详解卷三中描述),整体在1万的情况下,也才40M的内存浪费,CPU是会有定期的轮询检查以及端口分配时的消耗,基本可以忽略。

综上所述,TIME_WAIT在一定的量级上时基本可以不用管的,尤其时笔者所在的长连接的场景,正常情况下就更难出现。如果想要做更好的优化,下面有两个思路,

  1. 通过加速TIME_WAIT的回收

  2. 通过RST的方式替代之前的FIN报文进行四次握手,实现一次就能断开释放掉资源。

本篇文章主要介绍通过RST的方式。

如何使用RST来解决TCP断开问题

Linger的原理介绍

  1. SO_LINGER controls the action taken when unsent messages are queued on socket and a close(2) is performed. If the socket promises reliable delivery of data and SO_LINGER is set, the system will block the process on the close(2) attempt until it is able to transmit the data or until it decides it is unable to deliver the information (a timeout period, termed the linger interval, is specified in seconds in the setsockopt() system call when SO_LINGER is requested).

Linger选项的定义了当socket中还有未发送的数据的情况下,执行了close的动作之后,后续会由什么样的表现。如果开启的Linger选项的情况,close会阻塞直到系统传输万所有的数据,或者到达指定超时时间,执行了close之后,再收到对端的消息的时候,会直接回复RST报文。

实现RST的方式就是通过开启Linger选项,并且超时时间设置为0,这样的话,就能直接丢弃掉没有发送的数据,发出RST报文。

golang版本的代码验证

客户端代码

  1. package main

  2. import (

  3. "flag"

  4. "fmt"

  5. "net"

  6. "os"

  7. )

  8. var host = flag.String("host", "localhost", "host")

  9. var port = flag.String("port", "3333", "port")

  10. func main() {

  11. flag.Parse()

  12. conn, err := net.Dial("tcp", *host+":"+*port)

  13. if err != nil {

  14. fmt.Println("Error connecting:", err)

  15. os.Exit(1)

  16. }

  17. defer conn.Close()

  18. fmt.Println("Connecting to " + *host + ":" + *port)

  19. done := make(chan string)

  20. go handleWrite(conn, done)

  21. go handleRead(conn, done)

  22. fmt.Println(<-done)

  23. fmt.Println(<-done)

  24. }

  25. func handleWrite(conn net.Conn, done chan string) {

  26. _, e := conn.Write([]byte("hello \r\n"))

  27. if e != nil {

  28. fmt.Println("Error to send message because of ", e.Error())

  29. }

  30. done <- "Sent"

  31. }

  32. func handleRead(conn net.Conn, done chan string) {

  33. buf := make([]byte, 1024)

  34. reqLen, err := conn.Read(buf)

  35. if err != nil {

  36. fmt.Println("Error to read message because of ", err)

  37. done <- "Read Error"

  38. return

  39. }

  40. fmt.Println(string(buf[:reqLen-1]))

  41. done <- "Read"

  42. }

服务端代码

  1. package main

  2. import (

  3. "flag"

  4. "fmt"

  5. "net"

  6. "os"

  7. )

  8. var host = flag.String("host", "", "host")

  9. var port = flag.String("port", "3333", "port")

  10. func main() {

  11. flag.Parse()

  12. var l net.Listener

  13. var err error

  14. l, err = net.Listen("tcp", *host+":"+*port)

  15. if err != nil {

  16. fmt.Println("Error listening:", err)

  17. os.Exit(1)

  18. }

  19. // defer l.Close()

  20. fmt.Println("Listening on " + *host + ":" + *port)

  21. for {

  22. conn, err := l.Accept()

  23. if err != nil {

  24. fmt.Println("Error accepting: ", err)

  25. os.Exit(1)

  26. }

  27. //logs an incoming message

  28. fmt.Printf("Received message %s -> %s \n", conn.RemoteAddr(), conn.LocalAddr())

  29. // Handle connections in a new goroutine.

  30. go handleRequest(conn)

  31. }

  32. }

  33. func handleRequest(conn net.Conn) {

  34. defer conn.Close()

  35. buf := make([]byte, 1024)

  36. reqLen, err := conn.Read(buf)

  37. if err != nil {

  38. fmt.Println("Error to read message because of ", err)

  39. return

  40. }

  41. conn.Write(buf[:reqLen-1])

  42. fmt.Println(string(buf[:reqLen-1]))

  43. tcpConn, ok := conn.(*net.TCPConn)

  44. if !ok {

  45. fmt.Println("this is error", ok)

  46. }

  47. tcpConn.SetLinger(0)

  48. }

通过客户端的代码中添加了如下代码

  1. tcpConn.SetLinger(0)

完成设置Linger选项的功能,关于需要转换相应的类型,是因为对应的方法是TCPConn而不是net.Conn的原因。

正常TCP关闭过程

执行结果的抓包数据如下所示

通过报文可以看到,服务端在读取写入数据之后执行了正常的TCP四次挥手的关闭过程。

RST方式关闭过程

执行结果的抓包数据如下所示

通过报文可以看到,服务端在读取写入数据之后直接发送RST的报文终止整个的连接过程。

总结

通过以上的代码实现,以及抓包对报文的分析,可以看到通过Linger选项的设置,能够实现预期的通过RST的方式替代原有的FIN的关闭方式。对于Linger选项在各种实现方式下的不同表现,以及相应的限制,后续会持续的跟进。

RESET TCP and Linger

360云计算

由360云平台团队打造的技术分享公众号,内容涉及数据库、大数据、微服务、容器、AIOps、IoT等众多技术领域,通过夯实的技术积累和丰富的一线实战经验,为你带来最有料的技术分享

如何使用RST来解决TCP断开问题相关推荐

  1. TCP/IP详解 第十二章(9) TCP断开连接

    前言 不管面试 Java .C/C++.Python 等开发岗位, TCP 的知识点可以说是的必问的了. 任 TCP 虐我千百遍,我仍待 TCP 如初恋. 遥想小林当年校招时常因 TCP 面试题被刷, ...

  2. QUIC 是如何解决TCP 性能瓶颈的?

    作者:流云IoT 链接:https://blog.csdn.net/m0_37621078/article/details/106506532 重新整理:极客重生 文章目录 一.QUIC 如何解决TC ...

  3. tcp断开连接,4次握手,为什么wireshark 只能抓到3个包?

    用wireshark 抓包,看看tcp 断开连接的过程.  以前书上说tcp断开连接,4次握手,可我为什么wireshark 只能抓到3个包? 百度一下,别人也有类似的疑问. [求助]书上和网上的资料 ...

  4. xhtmlrenderer 将html转换成pdf,完美css,带图片,手动分页,解决内容断开的问题

    xhtmlrenderer 将html转换成pdf,完美css,带图片,手动分页,解决内容断开的问题 参考文章: (1)xhtmlrenderer 将html转换成pdf,完美css,带图片,手动分页 ...

  5. 范醒哲:5G时代是时候全面解决TCP的效率问题了

    回顾过去一年工业界在实时网络方面的探索,大量的篇幅留给了基于UDP的SRT.QUIC等明星协议,包括Google以及国内的B站都有令人欣喜的实践.但不可否认TCP在整个互联网依然占据统治地位,UDP并 ...

  6. c#解决TCP“粘包”问题

    c#解决TCP"粘包"问题 参考文章: (1)c#解决TCP"粘包"问题 (2)https://www.cnblogs.com/wangjun8868/p/71 ...

  7. golang解决TCP粘包问题

    6行代码解决golang TCP粘包 转自:https://studygolang.com/articles/12483 什么是TCP粘包问题以及为什么会产生TCP粘包,本文不加讨论.本文使用gola ...

  8. Netty解决TCP的粘包和分包(二)

    2019独角兽企业重金招聘Python工程师标准>>> Netty解决TCP的粘包和分包(二) 使用LengthFieldBasedFrameDecoder解码器分包 先看一下这个类 ...

  9. Linux socket编程(一):客户端服务端通信、解决TCP粘包

    一.服务端程序 服务端程序工作流程: 创建socket →\rightarrow→ 绑定监听的IP地址和端口 →\rightarrow→ 监听客户端连接 →\rightarrow→ 接受/发送数据.对 ...

最新文章

  1. 记一次阿里云RDS与自建数据库同步中断的补救过程
  2. HTML5 script 标签的 crossorigin 和integrity属性的作用
  3. 搜索1008(二分)
  4. MySQL使用GROUP_CONCAT分组拼接
  5. 【网络设计_RegNet】Designing Network Design Spaces_2020
  6. iOS-关于cell的重叠问题
  7. 【python】入门第一篇
  8. 洛谷(Python) P3717 [AHOI2017初中组]cover
  9. 解析解【闭式解(closed-form solution)】和数值解
  10. JAVA-基础(Stream流)
  11. 初识linux之vim工具与bdb调试工具
  12. CIA3 NOI接站(tarjan缩环+Floyd传递闭包+可相交最小路径覆盖)
  13. 基于Ubuntu20.04 GTX960m搭建cudacunn
  14. 默认语言及Android平台语言支持状态(印度语)Android N 设置中语言列表介绍
  15. 提高Linux下打开WPS云文档速度的方法
  16. 汽车一键启动开关发动机启动按钮点火开关图解
  17. Acer v5-573G笔记本拆键帽改dvorak教程
  18. java(springboot)实现闹钟功能(动态定时器)
  19. 程序员喝酒的计算机文化
  20. 一个ip 不同端口如何对应不同域名

热门文章

  1. 获取iview中表单组件Table的选中数据
  2. Springboot实现邮件发送(2020最新版)
  3. JavaScript(四)——具体对象(Math、字符串对象、Date对象、Number对象及Boolean对象)
  4. Docker镜像常用命令(三)
  5. tnsnames.ora配置未生效_nginx高可用配置未生效问题跟踪
  6. Array.prototype.slice.call 将伪数组转成真数组的原理是什么?
  7. 事务并发、事务隔离级别
  8. windows2003管理组创建
  9. vs2010 代码混淆 代码加密
  10. VBoxManage: error: Failed to create the host-only