Home > Blockchain >  Freeing a port before running a server instant
Freeing a port before running a server instant

Time:10-30

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!

hajsf@AIS-DM-YOUSEF-L:~/myapp$ go run myapp
Can't listen on port "8070": listen tcp :8070: bind: address already in use 
TCP Port "8070" is available
hajsf@AIS-DM-YOUSEF-L:~/myapp$ 

In the origional terminal (the old instance of the app0 I got;

hajsf@AIS-DM-YOUSEF-L:~/myapp$ go run myapp
signal: killed
hajsf@AIS-DM-YOUSEF-L:~/myapp$ 

enter image description here

CodePudding user response:

As you can see in the response How to kill a process running on particular port in Linux?

Your command _, err := exec.Command("fuser", "-k", "8070/tcp").Output() kill the process but doesn't cleanup the resource ie: port listening. The port is put into TIME_WAIT state after the parent process is killed.

And you need to wait some time your OS/Kernel cleanup the port/socket

A better alternative is to handle the kill sigint from fuser and do a graceful shutdown

package main

import (
    "context"
    "fmt"
    "net"
    "net/http"
    "os"
    "os/exec"
    "os/signal"
    "time"
)

func runServer(host, port string) {
    server := http.Server{
        Addr: host   ":"   port,
    }
    http.Handle("/www/", http.StripPrefix("/www/", http.FileServer(http.Dir("./www"))))
    go func() {
        if err := server.ListenAndServe(); err != nil {
        }
    }()

    // Setting up signal capturing
    stop := make(chan os.Signal, 1)
    signal.Notify(stop, os.Interrupt)

    // Waiting for SIGINT (kill -2)
    <-stop

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    if err := server.Shutdown(ctx); err != nil {
        // handle err
    }
}

func main() {

    host := "127.0.0.1"
    port := "8070"
    ln, err := net.Listen("tcp", host ":" 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
        cmd := exec.Command("fuser", "-k", "-2", "8070/tcp")
        fmt.Println("wait")
        err = cmd.Run()

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

    } else {
        ln.Close()
        runServer(host, port)
    }
}

CodePudding user response:

worked well on CentOS 7, but same issue with a Ubuntu server. @MoiioM 's answer is fine. But if another app is not the golang app itself, here is another way: set a SO_REUSEADDR flag on the socket

package main

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

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

func reusePort(network, address string, conn syscall.RawConn) error {
    return conn.Control(func(descriptor uintptr) {
        syscall.SetsockoptInt(int(descriptor), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
    })
}

func main() {

    port := "8070"
    server := http.Server{}

    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)
            config := &net.ListenConfig{Control: reusePort}
            listener, err := config.Listen(context.Background(), "tcp", ":" port)
            if err != nil {
                panic(err)
            }
            if err := server.Serve(listener); err != nil {
                panic(err)
            }
        }

    } else {
        if err := server.Serve(ln); err != nil {
            panic(err)
        }
    }
}
  •  Tags:  
  • go
  • Related