Introduction to Websockets
Websockets have enabled web apps to be much more concurrent in a scalable fashion, allowing for a push based model instead of a pull based model. In earlier days of web apps, when XmlHttpRequests were still new and exciting, the only way to communicate with your server was by sending web requests. If you wanted to get updates from the server, you generally had to repeatedly request updates, which was generally wasteful and made it difficult to write well synchronized web apps. All this has changed with the advent of Websockets.
Websockets provide an interface similar to a TCP socket, except in the web browser. Essentially, the client and server can both send messages at any time they want, and the other party will get woken up to handle the message. This is far more efficient than polling repeatedly, and is what makes the functionality of modern web apps such as Trello and Slack possible.
Getting started with Go
Google provides binary packages for GoLang at their website. On OS X and Windows you can set
OS X and LINUX:
On linux you can run “
. env.sh” to source the environment file. Source is a bash shell built-in command that executes the content of the file passed as argument, in the current shell. It is different from using “
./<filename>” as runs the script as an executable file, launching a new shell to run it, while using “
. <filename>” reads and executes the commands in the current shell. On windows run “
./env.ps1” to run the powershell file
If all goes well, you should see the following when you run ‘go’ on the shell. [code] Go is a tool for managing Go source code.
go command [arguments] ... [/code]
Starting a project in Go
Go follows a very strict directory structure, which you can read about here. In essence, the positions of everything under $GOPATH are proscribed by go. This is limiting in the sense that you can’t set special build directories, but freeing because you don’t have to worry about build tools disagreeing about how to compile code and where
Here is the example from the above page:
bin/ hello # command executable outyet # command executable pkg/ linux_amd64/ github.com/golang/example/ stringutil.a # package object src/ github.com/golang/example/ .git/ # Git repository metadata hello/ hello.go # command source outyet/ main.go # command source main_test.go # test source stringutil/ reverse.go # package source truncate.go reverse_test.go # test source
All go source code goes into the src directory. You’ll see that the files in hello and outyet are compiled into binaries, while the files in stringutil are compiled into a package. Go determines whether files consitute a command or a package based on whether they are listed as being in package “main” and whether they contain a “main” function.
To start our project, we are going to create a file called “main.go” in folder “godraw” under the “src” directory of our $GOPATH.
You’ll see that this file lists itself as being package ‘main’. We’ll get to that more later. For now, once you have written this file, run
go run main.go.
Dive into HTML
Also, because we’re now serving from the ‘public’ folder, modify your main function in main.go so that it serves the appropriate folder.
For the HTML, we just need to have a plain ‘canvas’ element on a blank page.
And we can use CSS to center everything in the center of the page.
This is a bit of a mouthful, so let’s go through it step by step.
This next part is the function to draw a point. It takes the positions we need, and draws a circle using the context’s “arc” method. You’ll see that we do this by drawing an arch around the point with an angle from 0 to 2PI, which completes the circle. The final “false” parameter indicates whether to draw clockwise or counterclockwise, which doesn’t matter for us.
The remaining lines just indicate the fill and stroke color, as well as the line width. The stroke indicates the color of the line surrounding the arc, and the fill indicates the color inside the arc. Finally, we have the line width, which is set to 0 since there is no point in making the circle bigger then our intended radius by having a stroke on the outside.
This is the code that handles drawing our circles. If the mouse is down, it draws a circle where the mouse is. In order to do it, we need to compute the position relative to the top-left corner of our canvas, which we can do by subtracting the canvas’ position on the page from the mouse’s position on the page.
A real server
The next part of this project will involve writing a websocket server that simply echoes the points you want to draw back on the same websocket. Once this is done, we will know that the client side of our drawing project will draw whatever it receives over the socket, and all that will be left is writing the server-side code to relay drawing commands to multiple pelople.
The first order of business is to make sure that we can handle WebSocket connections. We’re going to need to install the Gorilla WebSocket module, which you can do by running
go get github.com/gorilla/websocket
Next we add a global Upgrader and define a function “websocketHandler” that will ensure that we can in fact handle inbound websocket connections.
And add the following line to the top of your main function.
in the same area where you set up your context for drawing.
Now, restart the go server. Every time you load the page, the go program should print “Socket connected”.
Now that we have a websocket, we need the ability to send and receive points.
Add the following type to your main.go file:
This struct will be used to parse and send messages that represent points. In particular, this uses a language feature in go called ‘tags’. You’ll notice that X and Y are each ‘tagged’ with info to the JSON parset that tells it what name to deserialize and serializze properties to. This is necessary because we want X and Y on the point itself to be public, and in Go, public members have to start with an uppercase letter.
«««< Updated upstream Finally, to finish off our changes to the go server add the following function. ======= Finally, to finish off our changes to the go server, remove the “socketConnected” function, and add the following function.
Then change the websocketHandler function to call the echo server on as a new Goroutine (or lightweight thread)
Finally, your Go code should look like this:
This server will echo the points it receives over the websocket connection back to the client websocket.
The client side drawing code
That is all well and good, but our client still doesn’t know how to deal with messages from the server. The only two changes we need to make to have a working networked drawing client are have the client send the message to the server when you click and drag, and to do the actual drawing when it receives a messages from the server.
Modify the code in mouseMove to look like this:
Add the following code anywhere after the websocket is setup
This tells the websocket that any time it receives a message, it should call the messageReceived function, which will take the message that it got, deserialize it to a point, and then pass it to the drawPoint method.
What this means is that any time we draw in a program, we send a message to the server, and wait to draw until we get the echoed message back. This is inneficient for a single user, but makes programming a multi-person drawing program much simpler.
Your client code should now look like this.
A relay go server
Now we want our Go server to relay between all the connected clients. To do this, we’re going to set up a few more data structures. First, we’re going to create a relayServer type, so that we don’t have global state for our server.
You’ll notice a new type called ‘chan’ here. Channels are used in go to synchronize individual ‘GoRoutines’, or lightweight threads of execution. In this case, our server uses channels so that it can hear about new connections, closed connections, and points being input.
Next, we’ll need to add a type to manage each connection we receive.
This connection reverences the previous relayServer type, and also has the websocket connection itself, as well as an output channel so that the server can tell it to output points.
Now we need to write the code that glues all these types together.
For the server, we’ll add the following code:
These functions are slightly different from the other style of go functions. You’ll notice that they have a type and a name before the function name. This means that the function is a method, and like methods in java or C++, it operates on an object. Only instead of that object being called “this”, the object is given its own name in the declaration of the function. In this case, we call it ‘server’.
The ‘run’ function does two things. Primarily, it sends point messages that it receives out to the channels on all the clients. Also, any time a new client connects, all the point messages are replayed to that client so that they have all the same drawn points as another client.
The removeConnection, addConnection, and handleMessage methods are all helper methods that put values onto channels. By using channels here instead of directly operating on the data structure, we avoid all the issues with locking inherent in thread based code. There is a single Goroutine actually modifying the data structure, so there can’t be data races.
The final interesting method here is websocketHandler, which now tells the connection object itself to start receiving and sending messages.
Now we need to add the methods for the connection object as well.
Similar to the server object, we have a helper method to make it easy to send messages. The recieveMessages function sits in an infinit loop and sends any messages it recieves on to the server, while sendMessages listens on the outputChannel for messages to output from the server.
Now, since we’ve already written the client to rely on data from the server, you should be able ot run your server, and connect to it in two different tabs, or from another computer, and see that everything is synchronized!
Ideas for expansion
Now that you have drawing synchronized, you can use websockets to synchronize any data you want. You could put images in your canvas, or share urls to background music, or possibly let users SMS in messages that anyone can see. Websockets mean that what one user sees, all users see, creating a fluid and joyful web experience.