|
| 1 | +# Chat Example |
| 2 | + |
| 3 | +This application shows how to use the |
| 4 | +[websocket](https://github.com/gorilla/websocket) package to implement a simple |
| 5 | +web chat application. |
| 6 | + |
| 7 | +## Running the example |
| 8 | + |
| 9 | +The example requires a working Go development environment. The [Getting |
| 10 | +Started](http://golang.org/doc/install) page describes how to install the |
| 11 | +development environment. |
| 12 | + |
| 13 | +Once you have Go up and running, you can download, build and run the example |
| 14 | +using the following commands. |
| 15 | + |
| 16 | + $ go get github.com/gorilla/websocket |
| 17 | + $ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/chat` |
| 18 | + $ go run *.go |
| 19 | + |
| 20 | +To use the chat example, open http://localhost:8080/ in your browser. |
| 21 | + |
| 22 | +## Server |
| 23 | + |
| 24 | +The server application defines two types, `Client` and `Hub`. The server |
| 25 | +creates an instance of the `Client` type for each websocket connection. A |
| 26 | +`Client` acts as an intermediary between the websocket connection and a single |
| 27 | +instance of the `Hub` type. The `Hub` maintains a set of registered clients and |
| 28 | +broadcasts messages to the clients. |
| 29 | + |
| 30 | +The application runs one goroutine for the `Hub` and two goroutines for each |
| 31 | +`Client`. The goroutines communicate with each other using channels. The `Hub` |
| 32 | +has channels for registering clients, unregistering clients and broadcasting |
| 33 | +messages. A `Client` has a buffered channel of outbound messages. One of the |
| 34 | +client's goroutines reads messages from this channel and writes the messages to |
| 35 | +the websocket. The other client goroutine reads messages from the websocket and |
| 36 | +sends them to the hub. |
| 37 | + |
| 38 | +### Hub |
| 39 | + |
| 40 | +The code for the `Hub` type is in |
| 41 | +[hub.go](https://github.com/gorilla/websocket/blob/master/examples/chat/hub.go). |
| 42 | +The application's `main` function starts the hub's `run` method as a goroutine. |
| 43 | +Clients send requests to the hub using the `register`, `unregister` and |
| 44 | +`broadcast` channels. |
| 45 | + |
| 46 | +The hub registers clients by adding the client pointer as a key in the |
| 47 | +`clients` map. The map value is always true. |
| 48 | + |
| 49 | +The unregister code is a little more complicated. In addition to deleting the |
| 50 | +client pointer from the `clients` map, the hub closes the clients's `send` |
| 51 | +channel to signal the client that no more messages will be sent to the client. |
| 52 | + |
| 53 | +The hub handles messages by looping over the registered clients and sending the |
| 54 | +message to the client's `send` channel. If the client's `send` buffer is full, |
| 55 | +then the hub assumes that the client is dead or stuck. In this case, the hub |
| 56 | +unregisters the client and closes the websocket. |
| 57 | + |
| 58 | +### Client |
| 59 | + |
| 60 | +The code for the `Client` type is in [client.go](https://github.com/gorilla/websocket/blob/master/examples/chat/client.go). |
| 61 | + |
| 62 | +The `serveWs` function is registered by the application's `main` function as |
| 63 | +an HTTP handler. The handler upgrades the HTTP connection to the WebSocket |
| 64 | +protocol, creates a client, registers the client with the hub and schedules the |
| 65 | +client to be unregistered using a defer statement. |
| 66 | + |
| 67 | +Next, the HTTP handler starts the client's `writePump` method as a goroutine. |
| 68 | +This method transfers messages from the client's send channel to the websocket |
| 69 | +connection. The writer method exits when the channel is closed by the hub or |
| 70 | +there's an error writing to the websocket connection. |
| 71 | + |
| 72 | +Finally, the HTTP handler calls the client's `readPump` method. This method |
| 73 | +transfers inbound messages from the websocket to the hub. |
| 74 | + |
| 75 | +WebSocket connections [support one concurrent reader and one concurrent |
| 76 | +writer](https://godoc.org/github.com/gorilla/websocket#hdr-Concurrency). The |
| 77 | +application ensures that these concurrency requirements are met by executing |
| 78 | +all reads from the `readPump` goroutine and all writes from the `writePump` |
| 79 | +goroutine. |
| 80 | + |
| 81 | +To improve efficiency under high load, the `writePump` function coalesces |
| 82 | +pending chat messages in the `send` channel to a single WebSocket message. This |
| 83 | +reduces the number of system calls and the amount of data sent over the |
| 84 | +network. |
| 85 | + |
| 86 | +## Frontend |
| 87 | + |
| 88 | +The frontend code is in [home.html](https://github.com/gorilla/websocket/blob/master/examples/chat/home.html). |
| 89 | + |
| 90 | +On document load, the script checks for websocket functionality in the browser. |
| 91 | +If websocket functionality is available, then the script opens a connection to |
| 92 | +the server and registers a callback to handle messages from the server. The |
| 93 | +callback appends the message to the chat log using the appendLog function. |
| 94 | + |
| 95 | +To allow the user to manually scroll through the chat log without interruption |
| 96 | +from new messages, the `appendLog` function checks the scroll position before |
| 97 | +adding new content. If the chat log is scrolled to the bottom, then the |
| 98 | +function scrolls new content into view after adding the content. Otherwise, the |
| 99 | +scroll position is not changed. |
| 100 | + |
| 101 | +The form handler writes the user input to the websocket and clears the input |
| 102 | +field. |
0 commit comments