var upgrader = websocket.Upgrader{} // use default options
func socketHandler(w http.ResponseWriter, r *http.Request) {
// Upgrade our raw HTTP connection to a websocket based one
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("Error during connection upgradation:", err)
return
defer conn.Close()
// The event loop
for {
messageType, message, err := conn.ReadMessage()
if err != nil {
log.Println("Error during message reading:", err)
break
log.Printf("Received: %s", message)
err = conn.WriteMessage(messageType, message)
if err != nil {
log.Println("Error during message writing:", err)
break
func home(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Index Page")
func main() {
http.HandleFunc("/socket", socketHandler)
http.HandleFunc("/", home)
log.Fatal(http.ListenAndServe("localhost:8080", nil))
The magic that gorilla does is to convert these raw HTTP connections into a stateful websocket connection, using a
connection upgradation
. This is why the library uses a
struct
called
Upgrader
to help us with that.
We use a global upgrader variable to help us convert any incoming HTTP connection into websocket protocol, via
upgrader.Upgrade()
. This will return to us a
*websocket.Connection
, which we can now use to deal with the websocket connection.
The server reads messages using
conn.ReadMessage()
and writes them back using
conn.WriteMessage()
This server simply echoes any incoming websocket messages back to the client, so this shows how websockets can be used for
full-duplex
communication.
_, msg, err := connection.ReadMessage()
if err != nil {
log.Println("Error in receive:", err)
return
log.Printf("Received: %s\n", msg)
func main() {
done = make(chan interface{}) // Channel to indicate that the receiverHandler is done
interrupt = make(chan os.Signal) // Channel to listen for interrupt signal to terminate gracefully
signal.Notify(interrupt, os.Interrupt) // Notify the interrupt channel for SIGINT
socketUrl := "ws://localhost:8080" + "/socket"
conn, _, err := websocket.DefaultDialer.Dial(socketUrl, nil)
if err != nil {
log.Fatal("Error connecting to Websocket Server:", err)
defer conn.Close()
go receiveHandler(conn)
// Our main loop for the client
// We send our relevant packets here
for {
select {
case <-time.After(time.Duration(1) * time.Millisecond * 1000):
// Send an echo packet every second
err := conn.WriteMessage(websocket.TextMessage, []byte("Hello from GolangDocs!"))
if err != nil {
log.Println("Error during writing to websocket:", err)
return
case <-interrupt:
// We received a SIGINT (Ctrl + C). Terminate gracefully...
log.Println("Received SIGINT interrupt signal. Closing all pending connections")
// Close our websocket connection
err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
log.Println("Error during closing websocket:", err)
return
select {
case <-done:
log.Println("Receiver Channel Closed! Exiting....")
case <-time.After(time.Duration(1) * time.Second):
log.Println("Timeout in closing receiving channel. Exiting....")
return
If you observe the code, you’ll notice that I created two
channels
done
and
interrupt
for communication between
receiveHandler()
and
main()
.
We use an infinite loop for listening to events through channels using
select
. We write a message using
conn.WriteMessage()
every second. If the interrupt signal is activated, any pending connections are closed and we exit gracefully!
The nested
select
is there to ensure two things:
-
If the
receiveHandler
channel exits, the channel
'done'
will be closed. This is the first
case <-done
condition
-
If the
'done'
channel does NOT close, there will be a timeout after 1 second, so the program WILL exit after the 1 second timeout
By carefully handling all cases using channels,
select
, you can have a minimal architecture which can be extended easily.
Let’s finally look at the output we get, on running both the client and the server!
Sample Output
Go developer salary: factors impacting pay, job outlook, and how to create a perfect CV for this position
Scraping Amazon Products Data using Golang
Learning Golang with no programming experience
Remote Debugging in Golang and Java
Golang Read File Line by Line
Golang Global Variables
Techniques to Maximize Your Go Application’s Performance