Reconnecting Gorilla Websocket client on failure/lost connection

5,954 views
Skip to first unread message

a.i...@gmail.com

unread,
Mar 18, 2016, 10:37:25 AM3/18/16
to Gorilla web toolkit
Hi all,

I'm trying to figure out how to make a _reconnecting_ websocket client in golang with the Gorilla websocket library. I searched for examples but could not find them anywhere. I'm using the following code as a basis: https://github.com/gorilla/websocket/blob/master/examples/echo/client.go. Do you guys know of any existing example for this?

If I refactor this code to reconnect on failure or network problems I would place the connecting code in a separate function and call this when an error occurs in the ReadMessage part... The function for connecting will have a for loop and tries to connect at a given interval. I tried this implementation but I seem to miss edge cases for fails or write errors to the network. Any suggestions or recommendations based on the code from the given echo client? My code so far:


var (
    apiConnection  
*websocket.Conn
    apiConnected  
= false
)

func connectApi
() *websocket.Conn {
    u
:= url.URL{Scheme: "ws", Host: *addr, Path: "/ws"}
    log
.Printf("connecting to %s", u.String())

   
for {
        log
.Println("reconnecting")
       
var err error
        apiConnection
, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
       
if err != nil {
            apiConnected
= false
            log
.Println("dial: ", err)
            time
.Sleep(4 * time.Second)
           
continue
       
}

        log
.Println("API connected")
       
break
   
}

    apiConnected
= true
   
return apiConnection
}

func main
() {
    log
.SetFlags(0)

    interrupt
:= make(chan os.Signal, 1)
    signal
.Notify(interrupt, os.Interrupt)

   
// Connect to API
    apiConnection
= connectApi()
    defer apiConnection
.Close()

   
done := make(chan struct{})
    go func
() {
        defer apiConnection
.Close()
        defer close
(done)
       
for {
            _
, message, err := apiConnection.ReadMessage()
           
if err != nil {
                log
.Println("read: ", err)
                apiConnected
= false
                apiConnection
= connectApi()
                defer apiConnection
.Close()
               
return
           
}
            log
.Printf("recv: %s", message)
       
}
   
}()

   
for {
       
select {
       
case command := <-commands:
            log
.Println(command)
            err
:= apiConnection.WriteMessage(websocket.TextMessage, command)
           
if err != nil {
                log
.Println("command err: ", err)
               
return
           
}
       
case <-interrupt:
            log
.Println("interrupt")
           
// To cleanly close a connection, a client should send a close
           
// frame and wait for the server to close the connection.
            err
:= apiConnection.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
           
if err != nil {
                log
.Println("write close: ", err)
               
return
           
}
           
select {
           
case <-done:
           
case <-time.After(time.Second):
           
}
            apiConnected
= false
            apiConnection
.Close()
           
return
       
}
   
}
}

I hope you guys can help me out, thanks in advance for your time.

Eddy

Matt Silverlock

unread,
Mar 19, 2016, 12:14:26 PM3/19/16
to Gorilla web toolkit
You can inspect the error returned and decide how to handle specific cases - https://godoc.org/github.com/gorilla/websocket#IsCloseError
Reply all
Reply to author
Forward
0 new messages