diff --git a/examples/chat/README.md b/examples/chat/README.md index f0c365c..65ff69e 100644 --- a/examples/chat/README.md +++ b/examples/chat/README.md @@ -72,6 +72,17 @@ there's an error writing to the websocket connection. Finally, the HTTP handler calls the client's `readPump` method. This method transfers inbound messages from the websocket to the hub. +WebSocket connections [support one concurrent reader and one concurrent +writer](https://godoc.org/github.com/gorilla/websocket#hdr-Concurrency). The +application ensures that these concurrency requirements are met by executing +all reads from the `readPump` goroutine and all writes from the `writePump` +goroutine. + +To improve efficiency under high load, the `writePump` function coalesces +pending chat messages in the `send` channel to a single WebSocket message. This +reduces the number of system calls and the amount of data sent over the +network. + ## Frontend The frontend code is in [home.html](https://github.com/gorilla/websocket/blob/master/examples/chat/home.html). diff --git a/examples/chat/client.go b/examples/chat/client.go index c3c518d..bf6285d 100644 --- a/examples/chat/client.go +++ b/examples/chat/client.go @@ -49,6 +49,10 @@ type Client struct { } // readPump pumps messages from the websocket connection to the hub. +// +// The application runs readPump in a per-connection goroutine. The application +// ensures that there is at most one reader on a connection by executing all +// reads from this goroutine. func (c *Client) readPump() { defer func() { c.hub.unregister <- c @@ -70,13 +74,11 @@ func (c *Client) readPump() { } } -// write writes a message with the given message type and payload. -func (c *Client) write(mt int, payload []byte) error { - c.conn.SetWriteDeadline(time.Now().Add(writeWait)) - return c.conn.WriteMessage(mt, payload) -} - // writePump pumps messages from the hub to the websocket connection. +// +// A goroutine running writePump is started for each connection. The +// application ensures that there is at most one writer to a connection by +// executing all writes from this goroutine. func (c *Client) writePump() { ticker := time.NewTicker(pingPeriod) defer func() { @@ -86,13 +88,13 @@ func (c *Client) writePump() { for { select { case message, ok := <-c.send: + c.conn.SetWriteDeadline(time.Now().Add(writeWait)) if !ok { // The hub closed the channel. - c.write(websocket.CloseMessage, []byte{}) + c.conn.WriteMessage(websocket.CloseMessage, []byte{}) return } - c.conn.SetWriteDeadline(time.Now().Add(writeWait)) w, err := c.conn.NextWriter(websocket.TextMessage) if err != nil { return @@ -110,7 +112,8 @@ func (c *Client) writePump() { return } case <-ticker.C: - if err := c.write(websocket.PingMessage, []byte{}); err != nil { + c.conn.SetWriteDeadline(time.Now().Add(writeWait)) + if err := c.conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil { return } }