Fading Coder

An Old Coder’s Final Dance

Home > Tech > Content

Real-Time Messaging in Go with Gin and Gorilla WebSocket

Tech 1

This guide shows how to add WebSocket endpoints to a Gin-based Go service using Gorillla WebSocket, plus a minimal browser client to exercise both text and JSON message flows.

WebSocket libray

The server uses Gorilla WebSocket to the protocol upgrade and message I/O:

  • github.com/gorilla/websocket

Backend (Go)

The server exposes two WebSocket routes:

  • /ws/text: accepts a single text message and responds 10 times with an incrementing suffix
  • /ws/json: accepts a single JSON object and responds 10 times with the same fields plus a counter

Static files are served from ./public with index.html as the fallback.

package main

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		// Adjust for your deployment; this disables origin checks
		return true
	},
}

func handleText(c *gin.Context) {
	conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		log.Printf("upgrade failed: %v", err)
		return
	}
	defer conn.Close()

	msgType, payload, err := conn.ReadMessage()
	if err != nil {
		log.Printf("read failed: %v", err)
		return
	}

	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()

	for i := 1; i <= 10; i++ {
		<-ticker.C
		resp := fmt.Sprintf("%s %d", string(payload), i)
		if err := conn.WriteMessage(msgType, []byte(resp)); err != nil {
			log.Printf("write failed: %v", err)
			return
		}
	}
}

func handleJSON(c *gin.Context) {
	conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		log.Printf("upgrade failed: %v", err)
		return
	}
	defer conn.Close()

	type inMsg struct {
		A string `json:"a"`
		B int    `json:"b"`
	}
	var req inMsg
	if err := conn.ReadJSON(&req); err != nil {
		log.Printf("read json failed: %v", err)
		return
	}

	type outMsg struct {
		A string `json:"a"`
		B int    `json:"b"`
		C int    `json:"c"`
	}

	for i := 1; i <= 10; i++ {
		resp := outMsg{A: req.A, B: req.B, C: i}
		if err := conn.WriteJSON(resp); err != nil {
			log.Printf("write json failed: %v", err)
			return
		}
		time.Sleep(1 * time.Second)
	}
}

func main() {
	r := gin.Default()

	r.GET("/ws/text", handleText)
	r.GET("/ws/json", handleJSON)

	// Static files from ./public (place index.html here)
	r.Static("/", "./public")
	r.NoRoute(func(c *gin.Context) {
		c.File("./public/index.html")
	})

	if err := r.Run(":8000"); err != nil {
		log.Fatal(err)
	}
}

Place the following HTML at public/index.html.

Frontend (HTML)

The page opens two WebSocket connections on load, sends one message to each, and renders the replies as they arrive.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>WebSocket Demo</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
  </head>
  <body>
    <h1>WebSocket test</h1>
    <h2>JSON stream</h2>
    <pre id="json-log"></pre>
    <h2>Text stream</h2>
    <pre id="text-log"></pre>

    <script>
      document.addEventListener('DOMContentLoaded', () => {
        const scheme = location.protocol === 'https:' ? 'wss' : 'ws';
        const base = `${scheme}://${location.host}`;

        // JSON endpoint
        const j = new WebSocket(`${base}/ws/json`);
        j.addEventListener('open', () => {
          j.send(JSON.stringify({ a: 'bb', b: 2 }));
        });
        j.addEventListener('message', (evt) => {
          document.getElementById('json-log').textContent += evt.data + '\n';
        });

        // Text endpoint
        const t = new WebSocket(`${base}/ws/text`);
        t.addEventListener('open', () => {
          t.send('text message');
        });
        t.addEventListener('message', (evt) => {
          document.getElementById('text-log').textContent += evt.data + '\n';
        });
      });
    </script>
  </body>
</html>

Run the Go server and open http://localhsot:8000 in your browser. Each sent message results in 10 rseponses spaced one second apart.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.