Golang close net listener

7.6k views Asked by At

I am having trouble closing a listener so that I can reopen it on the same port. I am writing a proxy server that is hot-configurable - i.e what it does (redirects/blocks etc) can be adjusted on the fly. The proxy runs in a go routine. The problem I am having is when I reconfigure the listener and proxy the listener is still using that port from the previous configuration. (FYI listener is a global pointer) I'm trying something like:

to kill the listener:

func KillProxy() {
    
    if listener != nil {
        log.Println(" *** TRYING TO STOP THE PROXY SERVER")
        (*listener).Close()
        listener = nil
    }
}

before reconfiguring it like:

log.Println("listener (S1): ", listener)
if listener == nil {
    // Start the listener to listen on the connection.
    l, err := net.Listen(CONN_TYPE, CONN_HOST + ":" + CONN_PORT)

    if err != nil {
        log.Println("error here: ", err)
    }    
    listener = &l //set it as a pointer to the newly created listener
} 
log.Println("listener (S2): ", listener)

however that doesnt seem to work - I get the error:

listener (S1):

error here: listen tcp 0.0.0.0:8080: bind: address already in use

listener (S2): 0xc2080015e0

and then a massive stack trace that summizes to:

panic: runtime error: invalid memory address or nil pointer dereference

EDIT IN RESPONSE TO JIM:

Interesting re pointers. OK. I am not handling waiting for the socket to close, Im not sure how that should be done. The proxy library I am using is this one: https://github.com/abourget/goproxy. The order things happen is:

KillProxy()
refreshProxy()

refreshProxy holds the code posted above that tries to repurpose the listener. The last thing that happens in refreshProxy() is:

go http.Serve(*listener, proxy)

So if I revert to listener being a global variable, not a pointer I can make KillProxy():

func KillProxy() {
    if listener != nil {
        listener.Close()
        listener = nil
    }
}

and then setup the listener again as

listener, err := net.Listen(CONN_TYPE, CONN_HOST + ":" + CONN_PORT) 
    if err != nil {
        log.Println("error here: ", err)
    }    

However I don't know how to wait and check whether the socket has closed before trying to re create the listener object?

1

There are 1 answers

2
amlwwalker On

Ok this is far from perfect, but the only way I found of not having it error out on me was to artifically put a delay in my code so the socket had time to close. This isn't great as it means the proxy is down for 2 seconds while the socket closes, but at least it doesnt error out. If someone has a better solution I would like to hear it

func KillProxy() {
    if listener != nil {
        listener.Close()
        log.Println("waiting for socket to close")
        time.Sleep(2 * time.Second)
    }
}