Socket client and server in Golang with signal handling.
Continuing our series of articles about client/server, we will add a new channel to the previous example. This channel will handle signals sent by the operating system.
The operating system sends signals to programs to manage processes. For example, when you press Ctrl+C, the operating system sends the SIGINT signal, telling the program that the user wants to interrupt it.
By default, the program is closed. However, we can intercept this signal and act accordingly. In this case, we will simply terminate the program. In real systems, this feature can be used to save information before closing, stop other processes, close connections and files, and so on. This process is known as graceful shutdown.
Server
On the server, we create a channel and tell the system to send signals through it. We also create a goroutine to watch the channel.
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigs
fmt.Println("\nShutdown server...")
os.Exit(0)
}()
Client
On the client, the approach is different. We do not need to create a goroutine. Since we are already using select case to handle channels, we just add the new channel to the existing cases. It will be handled normally, just like the other channels.
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
for {
select {
case <-sigs:
fmt.Println("\nDisconnecting...")
conn.Close()
os.Exit(0)
...
Source code
Check out the source code of our server and client:
Videos with explanation
Conclusion
The change to add signal handling was simple but essential. When closing the program, it is important to make sure it is in a consistent state. For example, if the program saves data to a database, it is a good idea to wait for open transactions to complete before shutting the program down.