Writing a Websocket Chat in Go

October 29th, 2016 by

Learning the Go programming language,  I wanted to implement an application that I had written with other languages and frameworks before to get a grip on this language.

That’s why I tried to implement a really simple websocket chat server in Go and described my approach in the following article.

Go Websocket Chat Implementation

Go Websocket Chat Implementation


Writing the Chat Server

Websocket Chat in Action

Websocket Chat in Action

This is the implementation of our simple chat server.


Our chat server starts a new HTTP server that servers our static contents like HTML, CSS and JavaScript files from the directory static on port 8080.

In addition, the server initializes four chat rooms.

package chat
import (
// Run starts a new chat server with 4 chat rooms, listening on port 8080
func Run() {
	fs := http.FileServer(http.Dir("static"))
	http.Handle("/", fs)
	for _, name := range []string{"arduino", "java", "go", "scala"} {
		r := NewRoom(name)
		http.Handle("/chat/"+name, r)
		go r.Run()
	log.Printf("starting chat server on port 8080")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal("starting server failed: ", err)


Our chat room allows chatters to join, leave or talk in a room and offers convenience methods to create new rooms.

We’re using the Gorilla Websocket library here for our websocket connections to the client.

package chat
import (
const (
	socketBufferSize  = 1024
	messageBufferSize = 256
// Room represents a single chat room
type Room struct {
	forward chan []byte
	join chan *Chatter
	leave chan *Chatter
	chatters map[*Chatter]bool
	topic string
var upgrader = &websocket.Upgrader{
	ReadBufferSize:  socketBufferSize,
	WriteBufferSize: socketBufferSize,
	CheckOrigin: func(r *http.Request) bool {
		return true
func (r *Room) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	socket, err := upgrader.Upgrade(w, req, nil)
	if err != nil {
		log.Fatal("serving http failed ", err)
	chatter := &Chatter{
		socket: socket,
		send:   make(chan []byte, messageBufferSize),
		room:   r,
	r.join <- chatter
	defer func() {
		r.leave <- chatter
	go chatter.write()
// NewRoom creates a new chat room
func NewRoom(topic string) *Room {
	return &Room{
		forward:  make(chan []byte),
		join:     make(chan *Chatter),
		leave:    make(chan *Chatter),
		chatters: make(map[*Chatter]bool),
		topic:    topic,
// Run initializes a chat room
func (r *Room) Run() {
	log.Printf("running chat room %v", r.topic)
	for {
		select {
		case chatter := <-r.join:
			log.Printf("new chatter in room %v", r.topic)
			r.chatters[chatter] = true
		case chatter := <-r.leave:
			log.Printf("chatter leaving room %v", r.topic)
			delete(r.chatters, chatter)
		case msg := <-r.forward:
			data := FromJSON(msg)
			log.Printf("chatter '%v' writing message to room %v, message: %v", data.Sender, r.topic, data.Message)
			for chatter := range r.chatters {
				select {
				case chatter.send <- msg:
					delete(r.chatters, chatter)


Our chatter holds a reference to the selected chat room and delegates his messages to the specific chat room.

package chat
import "github.com/gorilla/websocket"
// Chatter represents a single Chatter
type Chatter struct {
	socket *websocket.Conn
	send   chan []byte
	room   *Room
func (c *Chatter) read() {
	for {
		if _, msg, err := c.socket.ReadMessage(); err == nil {
			c.room.forward <- msg
		} else {
func (c *Chatter) write() {
	for msg := range c.send {
		if err := c.socket.WriteMessage(websocket.TextMessage, msg); err != nil {


This structure is pretty useless as we’re only using it to display some information in our application logs but I wanted to demonstrate the possibilities for marshalling/unmarshalling JSON in Go.

package chat
import (
// Message represents a chat message
type Message struct {
	Message string `json:"message"`
	Sender  string `json:"sender"`
	Created string `json:"created"`
// FromJSON created a new Message struct from given JSON
func FromJSON(jsonInput []byte) (message *Message) {
	json.Unmarshal(jsonInput, &message)

Chat User Interface

The user  interface running in the browser consists of some simple HTML, CSS and JavaScript.

I’m just recycling the source from my tutorial “Creating a Chat Application using Java EE 7, Websockets and GlassFish 4“ here (but with the websocket port changed to 8080).

Client JavaScript

This simple JavaScript establishes the websocket connection and manages the client communication to the server and updates the user interface.

var wsocket;
var serviceLocation = "ws://";
var $nickName;
var $message;
var $chatWindow;
var room = '';
function onMessageReceived(evt) {
    var msg = JSON.parse(evt.data); // native API
    var $messageLine = $('<tr><td class="received">' + msg.received
            + '</td><td class="user label label-info">' + msg.sender
            + '</td><td class="message badge">' + msg.message
            + '</td></tr>');
function sendMessage() {
    var msg = '{"message":"' + $message.val() + '", "sender":"'
            + $nickName.val() + '", "received":""}';
function connectToChatserver() {
    room = $('#chatroom option:selected').val();
    wsocket = new WebSocket(serviceLocation + room);
    wsocket.onmessage = onMessageReceived;
function leaveRoom() {
$(document).ready(function() {
    $nickName = $('#nickname');
    $message = $('#message');
    $chatWindow = $('#response');
    $('#enterRoom').click(function(evt) {
        $('.chat-wrapper h2').text('Chat # '+$nickName.val() + "@" + room);
    $('#do-chat').submit(function(evt) {

HTML Template

This is just an excerpt, the full template is available at my repository here.

<div class="container chat-signin">
	<form class="form-signin">
		<h2 class="form-signin-heading">Chat sign in</h2>
		<label for="nickname">Nickname</label> <input type="text"
			class="input-block-level" placeholder="Nickname" id="nickname">
		<div class="btn-group">
			<label for="chatroom">Chatroom</label> <select size="1"
		<button class="btn btn-large btn-primary" type="submit"
			id="enterRoom">Sign in</button>
<div class="container chat-wrapper">
	<form id="do-chat">
		<h2 class="alert alert-success"></h2>
		<table id="response" class="table table-bordered"></table>
			<legend>Enter your message..</legend>
			<div class="controls">
				<input type="text" class="input-block-level" placeholder="Your message..." id="message" style="height:60px"/>
				<input type="submit" class="btn btn-large btn-block btn-primary"
					value="Send message" />
				<button class="btn btn-large btn-block" type="button" id="leave-room">Leave

Running the Chat

We’re now ready to build and run our chat application like this:

$ go get bitbucket.org/hascode/go-websocket-chat/chat
$ go run chat.go
2016/10/29 15:03:11 starting chat server on port 8080
2016/10/29 15:03:11 running chat room go
2016/10/29 15:03:11 running chat room scala
2016/10/29 15:03:11 running chat room arduino
2016/10/29 15:03:11 running chat room java
2016/10/29 15:03:56 new chatter in room go
2016/10/29 15:04:42 new chatter in room go
2016/10/29 15:05:09 new chatter in room go
2016/10/29 15:05:23 chatter 'Michelle' writing message to room go, message: Hello there
2016/10/29 15:05:33 chatter 'Tim' writing message to room go, message: Hey, what's up?
2016/10/29 15:08:15 chatter leaving room go
2016/10/29 15:08:18 chatter leaving room go

We may now access our chat in the browser at http://localhost:8080

Websocket Chat Login Screen

Websocket Chat Login Screen

Websocket Chat in Action

Websocket Chat in Action

Tutorial Sources

Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:

git clone https://bitbucket.org/hascode/go-websocket-chat.git


Websocket Chat Articles

I have written other tutorials about websocket server and client implementations, please feel to read further:

Tags: , , , , ,

2 Responses to “Writing a Websocket Chat in Go”

  1. turan_nn Says:

    thank you very much for your letter
    I want to close the chat room.
    how can I do that

  2. Dzintars Says:

    This example seems almost identical from the book Go: Design Patterns for Real-World Projects. And no credits.