5

I'm trying to modify the gorilla chat example to to send a message to a specific client instead of broadcast. First I'm storing the specific client in the hub against it's Id.

Hub.go

type Hub struct {
        Clients    map[int]*Client // Changed this piece to store id (int)
        Broadcast  chan []byte   
        Register   chan *Client   
        Unregister chan *Client   
}

func (h *Hub) Run() {
        for {
                    select {
                    case client := <-h.Register:
                                fmt.Println("hub client register")
                                h.Clients[client.Id] = client
                    case client := <-h.Unregister:
                                fmt.Println("hub client Unregister")
                                fmt.Println(h.Clients[client.Id])
                                if h.Clients[client.Id] != nil {
                                            delete(h.Clients, client.Id)
                                            close(client.Send)
                                }
                    case message := <-h.Broadcast:
                                fmt.Println("to send to a specific client", string(message))
                    }
        }
}

Client

I've added a field Id int to Client to know which client has sent a message

type Client struct {
        Hub  *Hub
        Conn *websocket.Conn
        Send chan []byte    
        Id   int // Id of the client,
}

func (c *Client) readPump() {
        defer func() {
                    c.Hub.Unregister <- c
                    c.Conn.Close()
        }()
        c.Conn.SetReadLimit(maxMessageSize)
        c.Conn.SetReadDeadline(time.Now().Add(pongWait))
        c.Conn.SetPongHandler(func(string) error { c.Conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
        for {
                    _, message, err := c.Conn.ReadMessage()
                    if err != nil {
                                if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
                                            log.Printf("error: %v", err)
                                }
                                break
                    }
                    message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))

                    fmt.Println("client read message", string(message), "from", c.Id)
        // {"to":512,"message":"Hi there."}
                    c.Hub.Broadcast <- message
        }
}

What are the next steps to take to be able to send the message to a specific client instead of broadcasting.

the message itself is coming as JSON from the client specifying 'to' indicating who to send and what message to send.

{"to":512,"message":"Hi there."}

1 Answer 1

3

Define a type representing a message:

type Message struct {
    id int
    data []byte
}

Add a field to Hub:

Send chan Message

and initialize the channel along with the hub's other channels.

Add the following case to the hub's select:

case m := <-h.Send:
    c, ok := clients[m.id]
    if ok {
        select {
        case c.send <- m.data:
        default:
           delete(h.Clients, c.Id)
           close(c.Send)
        }
    }

In the client's receive loop, parse the JSON to get the id and message data and send it to the hub:

    c.Hub.Send <- Message{id: id, data: data}
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks. Do we need default clause? I've unregistered handler in the hub doing the same thing. Please enlighten.
I'm also using gorilla, and something very similar to the above implementation. I'm looking to prevent a broadcast to the client who created the message. How would you accomplish that?
when I log "client" I get: &{0xc421084d50 0xc4210be280 0xc421066240 0} - how do you make this human readable?
By "receive loop" (in the answer above) do you mean the readPump func?
Looks like all my browser tabs have the same id - the chat works as expected. How do you get each client to have a unique id?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.