@nakem

Как сделать нормальное выключение Websocket сервера?

Мой вебсокет сервер сейчас просто вейтгруппой ждет пока соединения закроются. Я бы хотел, чтоб работало как в net/http Shutdown. Там функция каждое нное кол-во времени(кстати, почему рандомное) стучится, чтоб закрыть соединения, которые перестали подавать признаки жизни.
это часть Shutdown в 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())
		}
	}

это функция закрытия спящих соединений в 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
}
  • Вопрос задан
  • 83 просмотра
Решения вопроса 1
EvgenyMamonov
@EvgenyMamonov Куратор тега Go
Senior software developer, system architect
Если я правильно понял задачу - вам нужно корректно закрыть все соединения и завершить работу WebSocket сервера.

По сути аккуратное закрытие клиентского WebSocket соединения выглядит так:
- сервер отправляет в сокет frame с типом Close
- клиент при получении frame с типом Close формирует ответ с типом Close
- сервер ждёт ответ от клиента с типом Close (клиент подтверждает закрытие соединения)
- сервер со своей стороны закрываете сокет, клиент со своей стороны закрывает сокет

Т.е. вам просто нужно в цикле во все открытые сокеты отправить frame'ы с типом Close, дождаться ответа с типом Close, после чего закрыть сокет, или по таймауту закрыть сокет.

Для работы с websocket'ами я использовал вот этот пакет https://pkg.go.dev/github.com/gobwas/ws

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


Пример чтения ответа от клиента
header, err := ws.ReadHeader(wsconn)
if header.OpCode == ws.OpClose {
    // клиент подтвердил закрытие, соединение можно закрывать
    wsconn.Close()
}
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы