Регистрация обработчика URL-адреса http в секции обработчиков - PullRequest
2 голосов
/ 05 апреля 2019

Чтобы получить широковещательное сообщение от группы к нескольким обработчикам URL-адресов http, я пытаюсь зарегистрировать эти обработчики URL-адресов http, с кодом ниже в main.go:

type webSocketHandler func(http.ResponseWriter, *http.Request)

type threadSafeSlice struct {
    sync.Mutex
    handlers []*webSocketHandler
}

var sliceOfHandlers threadSafeSlice

func (slice *threadSafeSlice) push(handle *webSocketHandler) { //register

    slice.Lock()
    defer slice.Unlock()

    slice.handlers = append(slice.handlers, handle)
}

где forWardMsgToClient() - обработчик URL-адреса http, который необходимо зарегистрировать,

broadCastMessage() goroutine может передавать сообщения нескольким forWardMsgToClient() обработчикам, в следующем коде:

func main() {

    go broadcastMessage()
    http.HandleFunc("/websocket", forwardMsgToClient)
    http.ListenAndServe(":3000", nil)

}

func forwardMsgToClient(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    for {
         // Forward message to the client upon receiving a msg from publisher
    }
}

Весь приведенный выше код находится в main.go

Но проблема в том, что для соответствующего клиента порождается процедура forwardMsgToClient() после rw, e := l.Accept() вызова ../go/src/net/http/server.go.

Причина регистр (push()) Функция обработчика URL-адреса http (forwardMsgToClient()) заключается в том, чтобы broadcastMessage() goroutine знал число каналов, создаваемых для всех обработчиков URL-адресов http, и удалял канал, когда Отмена регистрации функции обработчика URL-адреса http (forwardMsgToClient()).


Немного нервничаю, если нам нужно изменить /go/src/net/http/server.go для достижения этой цели


Как зарегистрировать (push()) функцию обработчика http URL forwardMsgToClient() в sliceOfHandlers.handlers?

1 Ответ

2 голосов
/ 05 апреля 2019

Чтобы отправить сообщение всем подключенным клиентам веб-сокетов, выполните следующие действия:

  • Добавьте соединение в коллекцию при обновлении.
  • Удалите соединение из коллекции при подключениизакрыто.
  • Трансляция с помощью итерации по коллекции.

Простой подход:

type Clients struct {
    sync.Mutex
    m map[*websocket.Conn]struct{}
}

var clients = Clients{m: map[*websocket.Conn]struct{}{}}

func (cs *Clients) add(c *websocket.Conn) {
    cs.Lock()
    cs.m[c] = struct{}{}
    cs.Unlock()
}

func (cs *Clients) remove(c *websocket.Conn) {
    cs.Lock()
    delete(cs.m, c)
    cs.Unlock()
}

func (cs *Clients) broadcast(message []byte) {
    cs.Lock()
    defer cs.Unlock()
    for c, _ := range m {
       c.WriteMessage(websocket.TextMessage, message)
    }
}

Обработчик добавляет и удаляет соединения из коллекции следующим образом:

func forwardMsgToClient(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        // handle error
    }
    defer c.Close()
    clients.add(c)
    defer clients.remove(c)

    // Read the connection as required by the package.
    for {
        if _, _, err := c.NextReader(); err != nil {
            break
        }
    }
}

Чтобы отправить сообщение всемподключенные клиенты, звоните clients.broadcast(message).

. Этот простой подход не готов к работе по нескольким причинам: он не обрабатывает ошибку, возвращенную из WriteMessage, широковещание может блокироваться на зависшем клиенте.

Более надежное решение см. В Пример чата Gorilla .Концентратор вставляет канал между вещателем и соединением, таким образом позволяя концентратору вещать без блокировки.go broadcastMessage() в вопросе соответствует go hub.run() в примере с Gorilla.Обработчик forwardMsgToClient в вопросе создаст *client и отправит его на канал хаба register при обновлении и отправит этот *client на канал хаба unregister при разъединении.У *client есть канал, который перекачивается на соединение.

...