N
N
nakem2021-09-24 17:00:36
go
nakem, 2021-09-24 17:00:36

How to make a normal shutdown of the Websocket server?

My websocket server is now just waiting in a weightgroup for connections to close. I would like it to work like in net/http Shutdown. There, the function every given amount of time (by the way, why random) knocks to close connections that have ceased to show signs of life.

this is part of Shutdown in net/http
pollIntervalBase := time.Millisecond
  nextPollInterval := func() time.Duration {
    // Add 10% jitter.
    interval := pollIntervalBase + time.Duration(rand.Intn(int(pollIntervalBase/10)))
    // Double and clamp for next time.
    pollIntervalBase *= 2
    if pollIntervalBase > shutdownPollIntervalMax {
      pollIntervalBase = shutdownPollIntervalMax
    }
    return interval
  }

  timer := time.NewTimer(nextPollInterval())
  defer timer.Stop()
  for {
    if srv.closeIdleConns() && srv.numListeners() == 0 {
      return lnerr
    }
    select {
    case <-ctx.Done():
      return ctx.Err()
    case <-timer.C:
      timer.Reset(nextPollInterval())
    }
  }

this is the function of closing sleeping connections in net/http
// closeIdleConns closes all idle connections and reports whether the
// server is quiescent.
func (s *Server) closeIdleConns() bool {
  s.mu.Lock()
  defer s.mu.Unlock()
  quiescent := true
  for c := range s.activeConn {
    st, unixSec := c.getState()
    // Issue 22682: treat StateNew connections as if
    // they're idle if we haven't read the first request's
    // header in over 5 seconds.
    if st == StateNew && unixSec < time.Now().Unix()-5 {
      st = StateIdle
    }
    if st != StateIdle || unixSec == 0 {
      // Assume unixSec == 0 means it's a very new
      // connection, without state set yet.
      quiescent = false
      continue
    }
    c.rwc.Close()
    delete(s.activeConn, c)
  }
  return quiescent
}

Answer the question

In order to leave comments, you need to log in

1 answer(s)
E
Evgeny Mamonov, 2021-09-25
@nakem

If I understood the task correctly, you need to correctly close all connections and shut down the WebSocket server.
In fact, the accurate closing of a client WebSocket connection looks like this:
- the server sends a frame with the Close type to the socket
- the client, when receiving a frame with the Close type, generates a response with the Close type
- the server waits for a response from the client with the Close type (the client confirms the closing of the connection)
- the server with you close the socket on your side, the client closes the socket on its side
. you just need to send frames with the Close type to all open sockets in a loop, wait for a response with the Close type, and then close the socket, or close the socket by timeout.
To work with websockets, I used this packagehttps://pkg.go.dev/github.com/gobwas/ws
An example of sending a frame with the Close type

closeFrame := ws.NewCloseFrame([]byte{})
// отправляем 
err := ws.WriteFrame(wsconn, closeFrame)

Example of reading a response from a client
header, err := ws.ReadHeader(wsconn)
if header.OpCode == ws.OpClose {
    // клиент подтвердил закрытие, соединение можно закрывать
    wsconn.Close()
}

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question