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?
send() The WebSocket. send() method enqueues the specified data to be transmitted to the server over the WebSocket connection, increasing the value of bufferedAmount by the number of bytes needed to contain the data.
GitHub - gorilla/websocket: A fast, well-tested and widely used WebSocket implementation for Go. Skip to content Toggle navigation. Product.
Websocket client connections may drop due to intermittent network issue and when connections drop, messages will also be lost.
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}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With