Golang gracefully stop a tcp server
To gracefully stop a TCP server in Go, you can follow these steps:
Create a channel that will be used to signal the server to stop. For example:
stopChan := make(chan struct{})
Modify the server loop to check the stop channel before accepting new connections. For example:
for {
select {
case <-stopChan:
fmt.Println("Stopping server")
return
default:
conn, err := listener.Accept()
if err != nil {
fmt.Println(err)
continue
}
go handleConnection(conn)
}
}
To stop the server, send a value to the stop channel. For example:
stopChan <- struct{}{}
This will cause the server loop to exit, allowing the server to shut down gracefully.
Note that this is just one way to stop a TCP server in Go. Depending on your specific requirements, you may need to implement a different approach.
Use context
Here is an example of how you can use context to gracefully stop a TCP server:
package main
import (
"context"
"fmt"
"net"
"time"
)
func main() {
// Create a context with a timeout of 5 seconds
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() // Ensure the context is cancelled when we are done
// Start the server
ln, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println(err)
return
}
defer ln.Close()
// Accept connections in a loop
for {
// Check if the context is done
select {
case <-ctx.Done():
fmt.Println("Stopping the server")
return
default:
conn, err := ln.Accept()
if err != nil {
fmt.Println(err)
return
}
go handleConnection(conn)
}
}
}
func handleConnection(conn net.Conn) {
// Do something with the connection
// ...
conn.Close()
}
In this example, the server will stop accepting new connections and close all existing connections after 5 seconds. If you want to stop the server immediately, you can use context.WithCancel
instead of context.WithTimeout
and call the cancel function whenever you want to stop the server.
Also create a local context for each conn
:
package main
import (
"context"
"io"
"log"
"net"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
go func(ctx context.Context) {
listener, err := net.Listen("tcp", "0.0.0.0:8000")
if err != nil {
log.Fatalln("can't listen")
}
for {
select {
case <-ctx.Done():
listener.Close()
log.Println("listener closed")
return
default:
conn, err := listener.Accept()
if err != nil {
log.Println(err.Error())
}
go handle(ctx, conn)
}
}
}(ctx)
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGUSR1, syscall.SIGUSR2)
select {
case <-sigs:
cancel()
}
log.Println("signal received")
time.Sleep(10 * time.Minute)
}
func handle(ctx context.Context, conn net.Conn) error {
// create a local context which is canceled when the function returns
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// close the connection when the context is canceled
go func() {
<-ctx.Done()
conn.Close()
}()
// use the connection
_, err := io.Copy(os.Stdout(), conn)
return err
}
See Also
- Deal with sticky tcp/socket packet in Golang
- Get CPU Information with Pure Golang Code
- Golang upnp/ssdp DiscoverDevices example
- Golang Generics For Non-Beginners
- Pipe output from one shell command to another using golang