Reputation: 311
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 -
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?
Upvotes: 11
Views: 12463
Reputation: 120951
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}
Upvotes: 22