Go - WebSockets & SSE

Beginner 10/10 Teacher 10/10 Architect 10/10

WebSockets & Server-Sent Events (SSE)

Use WebSockets for bidirectional real-time messaging and SSE for unidirectional server-to-client streams.

Try it: Implement SSE that sends a timestamp every second.

SSE with net/http

package main

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

func sse(w http.ResponseWriter, r *http.Request){
  w.Header().Set("Content-Type", "text/event-stream")
  w.Header().Set("Cache-Control", "no-cache")
  w.Header().Set("Connection", "keep-alive")
  fl, _ := w.(http.Flusher)
  ticker := time.NewTicker(time.Second)
  defer ticker.Stop()
  for i:=0; i<5; i++ { // demo
    <-ticker.C
    fmt.Fprintf(w, "data: %s\n\n", time.Now().Format(time.RFC3339))
    if fl != nil { fl.Flush() }
  }
}

func main(){
  http.HandleFunc("/events", sse)
  http.ListenAndServe(":8080", nil)
}

WebSocket (concept)

You can implement WebSockets using a library (e.g., gorilla/websocket) or manually via net/http hijacking. Libraries handle frames and close semantics.

// Pseudocode with gorilla/websocket
// var upgrader = websocket.Upgrader{}
// func ws(w http.ResponseWriter, r *http.Request){
//   c, _ := upgrader.Upgrade(w, r, nil)
//   defer c.Close()
//   for {
//     _, msg, err := c.ReadMessage(); if err != nil { break }
//     c.WriteMessage(websocket.TextMessage, append([]byte("echo: "), msg...))
//   }
// }

Common errors

  • Forgetting to flush SSE responses.
  • Not handling client disconnects (browser closed) and breaking out of loops.
  • Missing CORS/CSRF considerations on WebSocket endpoints.

Practice

  • Add a retry field in SSE and test reconnect behavior in the browser.
  • Implement a ping/pong heartbeat for WebSockets.

Quick quiz

  1. When would you choose SSE over WebSockets?
Show answer When you only need server-to-client streaming, SSE is simpler and works over plain HTTP.