douwei2713 2019-01-21 12:35
浏览 646
已采纳

将Websocket消息发送到Go中的特定客户端(使用Gorilla)

I am very new to Go and have found myself working with sockets as my first project. This is a redundant question, but I have failed to understand how to send a websocket update to a specific client in Go (using Gorilla).

The broad problem that I am trying to solve is - Building a typeahead using websockets and a search engine like ES/Lucene. I have maintained a bunch of indexes on my search engine and have a Go wrapper around it. When I started working on using websockets in Go, I have been finding almost all the examples showing broadcasting mechanism. When I tried to dig into this and tried to modify the example given in Gorilla's github repo based on the examples given in this thread and in this answer, I don't seem to understand connections and how does that fit in client.go

Ideally, the way I would like to see this working is -

  • A socket connection between the client and server is established
  • Upon the client sending inputs via the socket, the server fetches it and throws into into a channel (Go channel)
  • The indexing wrapper checks for this channel, and once there is something to fetch, the index is retrieved and written back to the socket

How can the server uniquely identify the Client?

I have used the examples given on Gorilla's Github repo

From my codebase hub.go has the following

type Hub struct {
    // Registered clients.
    clients map[*Client]bool

    // Inbound messages from the clients.
    broadcast chan []byte

    // Register requests from the clients.
    register chan *Client

    // Unregister requests from clients.
    unregister chan *Client

    connections map[string]*connection
}

func newHub() *Hub {
    return &Hub{
        broadcast:  make(chan []byte),
        register:   make(chan *Client),
        unregister: make(chan *Client),
        clients:    make(map[*Client]bool),
        connection: make(map[*Client]bool), // is this alright?
    }
}

func (h *Hub) run() {
    for {
        select {
        case client := <-h.register:
            h.clients[client] = true
        case client := <-h.unregister:
            if _, ok := h.clients[client]; ok {
                delete(h.clients, client)
                close(client.send)
            }
        case message := <-h.broadcast:
            for client := range h.connections {
                select {
                case client.send <- message:
                default:
                    close(client.send)
                    delete(h.connections, client)
                }
            }
        }
    }
}

and I am unsure with what I should be adding to client.go

type Client struct {
    // unique ID for each client
    // id string

    // Hub object
    hub *Hub

    // The websocket connection.
    conn *websocket.Conn

    // Buffered channel of outbound messages.
    send chan []byte

    // connection --> (what should the connection property be?)
    connection string
}

Please note - I will be adding an Id field within the Client struct. How can I proceed from here?

  • 写回答

1条回答

  • doutan1671 2019-01-21 16:49
    关注

    The chat example shows how to implement broadcast. The chat example is not a good starting point for an application if broadcast is not required.

    To send a message to a specific websocket connection, simply write to the connection using NextWriter or WriteMessage. These methods do not support concurrent writers, so you may need to use a mutex or goroutine to ensure a single writer.

    The simple approach for finding a specific *websocket.Connection is to pass *websocket.Connection to the code that needs it. If the application needs to associate other state with a connection, then define a type to hold that state and pass a pointer to that around:

    type Client struct {
        conn *websocket.Conn
        mu sync.Mutex
        ...
    }
    

    The Hub can be modified to send messages to specific connection, but it's a roundabout path if broadcast is not needed. Here's how to do it:

    Add ID field to client:

     ID idType // replace idType with int, string, or whatever you want to use
    

    Change the Gorilla hub field from connections map[*connection]bool to connections map[idType]*connection.

    Define a message type containing the message data and the ID of the target client:

    type message struct {
       ID idtype
       data []byte
    }
    

    Replace the hub broadcast field with:

       send chan message
    

    Change the hub for loop to:

    for {
        select {
        case client := <-h.register:
            h.clients[client.ID] = client
        case client := <-h.unregister:
            if _, ok := h.clients[client.ID]; ok {
                delete(h.clients, client.ID)
                close(client.send)
            }
        case message := <-h.send:
            if client, ok := h.clients[message.ID]; ok {
                select {
                case client.send <- message.data:
                default:
                    close(client.send)
                    delete(h.connections, client)
                }
            }
        }
    

    Send messages to a specific client by creating a message with the appropriate ID:

       hub.send <- message{ID: targetID, data: data}
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 R语言Rstudio突然无法启动
  • ¥15 关于#matlab#的问题:提取2个图像的变量作为另外一个图像像元的移动量,计算新的位置创建新的图像并提取第二个图像的变量到新的图像
  • ¥15 改算法,照着压缩包里边,参考其他代码封装的格式 写到main函数里
  • ¥15 用windows做服务的同志有吗
  • ¥60 求一个简单的网页(标签-安全|关键词-上传)
  • ¥35 lstm时间序列共享单车预测,loss值优化,参数优化算法
  • ¥15 Python中的request,如何使用ssr节点,通过代理requests网页。本人在泰国,需要用大陆ip才能玩网页游戏,合法合规。
  • ¥100 为什么这个恒流源电路不能恒流?
  • ¥15 有偿求跨组件数据流路径图
  • ¥15 写一个方法checkPerson,入参实体类Person,出参布尔值