package main import ( "log" "sync" "time" "github.com/gorilla/websocket" ) const TIMEOUT = 6 const IDLE_TIMEOUT = TIMEOUT * time.Second const PING_INTERVAL = (TIMEOUT - 1) * time.Second type WSConnection struct { url string conn *websocket.Conn errChan chan error writeLock sync.Mutex ReadChan chan string WriteChan chan string } func (ws *WSConnection) messageReader() { log.Printf("Reading messages") for { _, message, err := ws.conn.ReadMessage() ws.conn.SetReadDeadline(time.Now().Add(IDLE_TIMEOUT)) if err != nil { ws.errChan <- err return } log.Printf("Received: %s", message) ws.ReadChan <- string(message) } } func (ws *WSConnection) messageSender() { log.Printf("Sending messages") for { msg := <-ws.WriteChan ws.writeLock.Lock() ws.conn.SetWriteDeadline(time.Now().Add(IDLE_TIMEOUT)) log.Printf("Sending: %s", msg) err := ws.conn.WriteMessage(websocket.TextMessage, []byte(msg)) if err != nil { log.Printf("Error during message writing: %v", err) ws.errChan <- err return } ws.writeLock.Unlock() } } func (ws *WSConnection) pinger() { log.Printf("Starting pinger, sleeping for %v", PING_INTERVAL) for { time.Sleep(PING_INTERVAL) // log.Printf("Ping") ws.writeLock.Lock() err := ws.conn.WriteMessage(websocket.PingMessage, nil) if err != nil { log.Println("Error during ping:", err) ws.errChan <- err return } ws.writeLock.Unlock() } } func (ws *WSConnection) handleError() { for { err := <-ws.errChan log.Println("Error during message reading:", err) time.Sleep(5 * time.Second) ws.Open() } } func (ws *WSConnection) Open() { log.Printf("Connecting to %s", ws.url) conn, _, err := websocket.DefaultDialer.Dial(ws.url, nil) if err != nil { log.Println("Error during connection:", err) ws.errChan <- err return } log.Printf("Connected") ws.conn = conn ws.errChan = make(chan error) ws.ReadChan = make(chan string, 1024) ws.WriteChan = make(chan string, 1024) ws.conn.SetReadLimit(1024) ws.conn.SetReadDeadline(time.Now().Add(IDLE_TIMEOUT)) ws.conn.SetWriteDeadline(time.Now().Add(IDLE_TIMEOUT)) ws.conn.SetPongHandler(func(string) error { // log.Println("Pong") ws.conn.SetReadDeadline(time.Now().Add(IDLE_TIMEOUT)) ws.conn.SetWriteDeadline(time.Now().Add(IDLE_TIMEOUT)) return nil }) go ws.messageReader() go ws.messageSender() go ws.handleError() go ws.pinger() }