1

I'm having the following server code, which listens via unix domain socket

package main

import (
    "log"
    "net"
    "os"
    "os/signal"
    "syscall"
)

func echoServer(c net.Conn) {
    for {
        buf := make([]byte, 512)
        nr, err := c.Read(buf)
        if err != nil {
            return
        }

        data := buf[0:nr]
        println("Server got:", string(data))
        _, err = c.Write(data)
        if err != nil {
            log.Fatal("Writing client error: ", err)
        }
    }
}

func main() {
    log.Println("Starting echo server")
    ln, err := net.Listen("unix", "/tmp/go.sock")
    if err != nil {
        log.Fatal("Listen error: ", err)
    }

    sigc := make(chan os.Signal, 1)
    signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)
    go func(ln net.Listener, c chan os.Signal) {
        sig := <-c
        log.Printf("Caught signal %s: shutting down.", sig)
        ln.Close()
        os.Exit(0)
    }(ln, sigc)

    for {
        fd, err := ln.Accept()
        if err != nil {
            log.Fatal("Accept error: ", err)
        }

        go echoServer(fd)
    }
}

When I close it using Ctrl+C, then the signal is captured and the socket is closed. When I rerun the program, everything works fine.

However, if the running process is abruptly killed, and if the program is restarted, the listen fails with the error Listen error: listen unix /tmp/go.sock: bind: address already in use

How to graciously handle this?

The reason why I ask this is: I know that abruptly killing is not the normal method, but my program shall be launched automatically as a daemon and if my daemon is restarted, I want to be able to listen to the socket again without this error.

It could also be because of a prior instance running, which I understand. The question here is how to programmatically identify in Go and handle this situation. As pointed in the answer here, one can use SO_REUSEADDR in C programs. Is there such a possibility in Go? Also, how do C programs handle this multiple instance problem.

7
  • Servers don't connect. Clients connect. Servers listen and accept connections. If you get 'address already in use when binding, the address is already in use, possibly by a prior instance of your program, possibly because 'abruptly killing' is not the way to terminate it. Commented May 14, 2017 at 10:29
  • @EJP Wow.. -1 for a typo, where I had written connect instead of listen. That is funny that you say that abruptly killing is not the way to terminate it. I know that. However, when a customer who is using my program somehow kills it, I want to think how to handle it graceful! In C, I can use reuse socket, but how to do that in Go in the question here. Think before you give negative points to a question! Commented May 14, 2017 at 10:31
  • 2
    SO_REUSEADDR isn't relevant here (see stackoverflow.com/questions/15716302/so-reuseaddr-and-af-unix). Delete the file if you don't need it and want to bind to that address again. Commented May 14, 2017 at 11:53
  • See this related question. In short, also handle os.Kill and syscall.SIGTERM signal in signal.Notify. Commented May 14, 2017 at 12:08
  • 2
    This answer explains how to detect if a working instance is listening, and its question has more details on why to delete the unix domain socket: stackoverflow.com/a/13719866/7496656 Commented May 14, 2017 at 19:54

1 Answer 1

0

You need to catch the signal and cleanup; some example code:

func HandleSIGINTKILL() chan os.Signal {
    sig := make(chan os.Signal, 1)

    signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)

    return sig
}

...
go func() {
    <-HandleSIGINTKILL()

    log.Info("Received termination signal")
    // Cleanup code here

    os.Exit(0)
}()

This will of course not work if you kill -9 the process; you will need to manually remove the socket (or have your init system do it for you).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.