How to Replace TCP FIN with RST Using SO_LINGER in Go
This article explains why TIME_WAIT occurs, its resource impact, and demonstrates two optimization strategies—accelerating TIME_WAIT reclamation and using the SO_LINGER option to send an RST packet instead of the normal FIN handshake, with complete Go client and server examples and packet‑capture analysis.
Introduction
During a recent team sharing on long‑connection services, a solution for handling TIME_WAIT was discussed. This article provides a concise overview of TIME_WAIT, its causes, resource impact, and two optimization ideas, focusing on the RST‑based approach.
TIME_WAIT Overview
Only the side that initiates the connection closure enters the TIME_WAIT state.
A socket in TIME_WAIT occupies a small amount of memory (approximately 4 KB per socket) and minimal CPU for periodic checks; even with 10 000 sockets the total memory consumption is around 40 MB, which is generally negligible.
Therefore, when the number of TIME_WAIT sockets is moderate, it can be ignored, especially in typical long‑connection scenarios.
Optimization Strategies
Accelerate TIME_WAIT reclamation.
Replace the four‑step FIN handshake with a single RST packet by using the SO_LINGER option.
This article focuses on the second method.
Using RST via SO_LINGER
The SO_LINGER socket option controls the behavior of close() when there is unsent data. Setting SO_LINGER with a timeout of 0 forces the socket to discard pending data and immediately send an RST packet, terminating the connection without entering TIME_WAIT.
Go Client Example
package main
import (
"flag"
"fmt"
"net"
"os"
)
var host = flag.String("host", "localhost", "host")
var port = flag.String("port", "3333", "port")
func main() {
flag.Parse()
conn, err := net.Dial("tcp", *host+":"+*port)
if err != nil {
fmt.Println("Error connecting:", err)
os.Exit(1)
}
defer conn.Close()
fmt.Println("Connecting to "+*host+":"+*port)
done := make(chan string)
go handleWrite(conn, done)
go handleRead(conn, done)
fmt.Println(<-done)
fmt.Println(<-done)
}
func handleWrite(conn net.Conn, done chan string) {
_, e := conn.Write([]byte("hello \r\n"))
if e != nil {
fmt.Println("Error to send message because of ", e.Error())
}
done <- "Sent"
}
func handleRead(conn net.Conn, done chan string) {
buf := make([]byte, 1024)
reqLen, err := conn.Read(buf)
if err != nil {
fmt.Println("Error to read message because of ", err)
done <- "Read Error"
return
}
fmt.Println(string(buf[:reqLen-1]))
done <- "Read"
}Go Server Example
package main
import (
"flag"
"fmt"
"net"
"os"
)
var host = flag.String("host", "", "host")
var port = flag.String("port", "3333", "port")
func main() {
flag.Parse()
l, err := net.Listen("tcp", *host+":"+*port)
if err != nil {
fmt.Println("Error listening:", err)
os.Exit(1)
}
fmt.Println("Listening on "+*host+":"+*port)
for {
conn, err := l.Accept()
if err != nil {
fmt.Println("Error accepting:", err)
os.Exit(1)
}
go handleRequest(conn)
}
}
func handleRequest(conn net.Conn) {
defer conn.Close()
buf := make([]byte, 1024)
reqLen, err := conn.Read(buf)
if err != nil {
fmt.Println("Error to read message because of ", err)
return
}
fmt.Println(string(buf[:reqLen-1]))
// Send response
conn.Write(buf[:reqLen-1])
// Convert to TCPConn to set linger
if tcpConn, ok := conn.(*net.TCPConn); ok {
tcpConn.SetLinger(0) // Force RST on close
}
}Packet Capture: Normal TCP Close
The following capture shows the server performing the standard four‑step TCP termination after reading and writing data.
Packet Capture: RST Close
When SO_LINGER is set to 0 , the server immediately sends an RST packet, terminating the connection without the FIN handshake.
Conclusion
By configuring the SO_LINGER option with a timeout of 0 , the connection can be closed using an RST packet, effectively bypassing the TIME_WAIT state. This technique is useful for optimizing long‑connection services where rapid teardown is required. Further exploration of Linger behavior across different platforms will be continued.
360 Zhihui Cloud Developer
360 Zhihui Cloud is an enterprise open service platform that aims to "aggregate data value and empower an intelligent future," leveraging 360's extensive product and technology resources to deliver platform services to customers.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.