Freeing a port before running a server instant

I want to run a go server at linux based system, it happened in soe cases that i found the same port is busy with another app, so i want to kill the running process at that port, and run my server instead, so I wrote the below code:

func main() {

	host := "127.0.0.1"
	port := "8070"
	server := http.Server{
		Addr: "127.0.0.1:8070",
	}

	http.Handle("/www/", http.StripPrefix("/www/", http.FileServer(http.Dir("./www"))))

	ln, err := net.Listen("tcp", ":"+port)

	if err != nil {
		fmt.Fprintf(os.Stderr, "Can't listen on port %q: %s \n", port, err)
        // kill the running process at this port
		_, err := exec.Command("fuser", "-k", "8070/tcp").Output()

		if err != nil {
			fmt.Printf("Failed to kill process at Port %q\n", port)
		} else {
			fmt.Printf("TCP Port %q is available\n", port)
			server.ListenAndServe()
		}

	} else {
		ln.Close()
		server.ListenAndServe()
	}
}

I was able to get the response TCP Port 8070 is available whihc means there was another running process and it had been killed, but my app is closed directly without running my server at the same port which had been already closed!

I am curious. Why same port if there are 65,535 other ports to chose?

Check the returned error. It’s there for a reason.

I was able to solve it with panic ... recover as below:

package main

import (
	"fmt"
	"net"
	"net/http"
	"onsen/resources"
	"onsen/routes"
	"os"
	"os/exec"
)

func main() {
	http.Handle("/webUI/", http.StripPrefix("/webUI/", http.FileServer(http.FS(resources.WebUI))))
	http.Handle("/www/", http.StripPrefix("/www/", http.FileServer(http.Dir("./www"))))

	for key, value := range routes.Urls() {
		http.HandleFunc(key, value)
	}
	runServer()
}
func runServer() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Recovered in f", r)
			runServer()
		}
	}()

	host := "127.0.0.1"
	port := "8070"

	server := http.Server{
		Addr: fmt.Sprintf("%v:%v", host, port),
	}

	ln, err := net.Listen("tcp", ":"+port)

	if err != nil {
		fmt.Fprintf(os.Stderr, "Can't listen on port %q: %s \n", port, err)
		// kill the running process at this port
		_, err := exec.Command("fuser", "-k", "8070/tcp").Output()

		if err != nil {
			panic(fmt.Sprintf("Failed to kill process at Port %q\n", port))
		} else {
			fmt.Printf("TCP Port %q is available\n", port)
			fmt.Println("server started...")
			if err := server.Serve(ln); err != nil {
				panic(err)
			}
		}
	} else {
		fmt.Println("server started...")
		if err := server.Serve(ln); err != nil {
			panic(err)
		}
	}
}

And got the output as below:

ln is still nil. try to listen again

linux/unix systems keep port reserved for some time (1min) after it was used.
This is because if client tries to connect and some other service starts and uses same port client should notice service was closed.
see e.g Reusing address in socket - how does it actually work ? - CodeProject
try setting reuse address/port socket option to listening socket.
see e.g Go v1.11 net.Listener SO_REUSEPORT or SO_REUSEADDR example · GitHub

2 Likes

mmm, not getting you, can you give sample code lines, thanks

the point of code below (not fully tested) is go get socket file descriptor and set options to it before actual listen is called.

package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"syscall"

	"golang.org/x/sys/unix"
)

func main() {
	runServer()
}
func runServer() {

	host := "127.0.0.1"
	port := "8070"

	ctx := context.Background()

	server := http.Server{
		Addr: host + ":" + port,
	}
	defer func() {
		_ = server.Shutdown(ctx)
	}()

	lc := net.ListenConfig{
		Control: setReusePort,
	}
	ln, err := lc.Listen(ctx, "tcp4", host+":"+port)
	if err != nil {
		return
	}
	defer ln.Close()

	if err := server.Serve(ln); err != nil {
		return
	}
}

func setReusePort(network, address string, c syscall.RawConn) error {
	c.Control(rawConnSetReusePort)
	return nil
}

func rawConnSetReusePort(fd uintptr) {
	syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1)
}

also note the server.Shutdown().
ln.Close() is litle bit overkill.

1 Like