Master Real-Time Communication: Build WebSocket Apps in Go
This guide explains how WebSockets enable full‑duplex communication over a single TCP connection, compares popular Go WebSocket libraries, and provides complete client‑server code examples for TCP, UDP, and HTTP‑based WebSocket implementations, helping developers build real‑time applications such as chat, games, and monitoring tools.
Translated from: How to Use Websockets in Golang
Sending messages and receiving instant responses without refreshing the page is now taken for granted, but building real‑time features used to be a major challenge. WebSockets open an interactive session between a browser and a server, allowing full‑duplex communication without repeated polling. They are the preferred solution for online games, instant messaging, tracking apps, and other real‑time services.
Network Sockets and WebSockets
Network Sockets
Network sockets (or simply sockets) are internal endpoints used for data exchange between applications on the same machine or across a network. They are a core part of Unix and Windows operating systems, simplifying the creation of network‑enabled software. Sockets can be created via the socket API and come in several types:
Datagram socket (SOCK_DGRAM) – connection‑less, uses UDP, preserves message boundaries.
Stream socket (SOCK_STREAM) – connection‑oriented, uses TCP (or SCTP/DCCP), provides reliable, ordered, non‑duplicate byte streams.
Raw socket – typically used by routers and low‑level network tools; rarely needed by application developers.
Socket Communication
Each socket is identified by a triple: transport protocol, IP address, and port number. The two main transport protocols are TCP and UDP.
Connect to TCP socket
Go clients use net.DialTCP to establish a TCP connection, which returns a TCPConn object. After the connection is established, the client sends requests via the TCPConn and reads responses from the server.
// init
tcpAddr, err := net.ResolveTCPAddr(resolver, serverAddr)
if err != nil {
// handle error
}
conn, err := net.DialTCP(network, nil, tcpAddr)
if err != nil {
// handle error
}
// send message
_, err = conn.Write(message)
if err != nil {
// handle error
}
// receive message
var buf [buffSize]byte
_, err = conn.Read(buf[:])
if err != nil {
// handle error
}Server side:
// init
tcpAddr, err := net.ResolveTCPAddr(resolver, serverAddr)
if err != nil {
// handle error
}
listener, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
// handle error
}
conn, err := listener.Accept()
if err != nil {
// handle error
}
// send message
if _, err := conn.Write(message); err != nil {
// handle error
}
// receive message
buf := make([]byte, 512)
n, err := conn.Read(buf[:])
if err != nil {
// handle error
}Connect to UDP socket
Unlike TCP, a UDP client only sends datagrams to the server; there is no Accept call on the server side.
// init
raddr, err := net.ResolveUDPAddr("udp", address)
if err != nil {
// handle error
}
conn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
// handle error
}
// send message
// ...
// receive message
n, addr, err := conn.ReadFrom(buffer)
if err != nil {
// handle error
}Server side:
// init
udpAddr, err := net.ResolveUDPAddr(resolver, serverAddr)
if err != nil {
// handle error
}
conn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
// handle error
}
// send message
// ...
// receive message
n, addr, err := conn.ReadFromUDP(buffer)
if err != nil {
// handle error
}What is WebSocket
WebSocket communication provides a full‑duplex channel over a single TCP connection, allowing both client and server to send data at any time without additional requests. It is ideal for services that require continuous data exchange, such as instant messaging, online games, and real‑time trading systems. The protocol is defined in IETF RFC 6455.
A WebSocket connection is established via an HTTP handshake; after the handshake the connection remains open for its entire lifetime. Unsecure connections use port 80, secure connections use port 443. The protocol defines the ws and wss URI schemes.
Lightweight headers reduce transmission overhead.
A single TCP connection per client.
Servers can push data to clients.
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.comServer response:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chatHow to Create a WebSocket Application in Go
To build a simple WebSocket echo server with the net/http library you need to:
Initiate the handshake.
Receive data frames from the client.
Send data frames to the client.
Close the handshake.
First, create an HTTP handler with a WebSocket endpoint:
// HTTP server with WebSocket endpoint
func Server() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
ws, err := NewHandler(w, r)
if err != nil {
// handle error
}
if err = ws.Handshake(); err != nil {
// handle error
}
// ... handle communication ...
})
}The handshake uses the standard HTTP request headers and then upgrades the connection to raw TCP for bidirectional reads and writes.
// Handshake creates a handshake header
func (ws *WS) Handshake() error {
hash := func(key string) string {
h := sha1.New()
h.Write([]byte(key))
h.Write([]byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}(ws.header.Get("Sec-WebSocket-Key"))
// ... send response with Sec-WebSocket-Accept: hash ...
return nil
}After a successful handshake, the application can read and write frames. The WebSocket specification defines the exact bit layout of frames.
// Recv receives data and returns a Frame
func (ws *WS) Recv() (frame Frame, _ error) {
head, err := ws.read(2)
if err != nil {
// handle error
}
// ... parse header ...
return frame, nil
}
// Send sends a Frame
func (ws *WS) Send(fr Frame) error {
data := make([]byte, 2)
data[0] = 0x80 | fr.Opcode
if fr.IsFragment {
data[0] &= 0x7F
}
// ... write payload ...
return nil
}Close Handshake
When either side sends a close frame, the connection is terminated after an optional close reason is exchanged.
// Close sends a close frame and closes the TCP connection
func (ws *Ws) Close() error {
f := Frame{}
f.Opcode = 8
f.Length = 2
f.Payload = make([]byte, 2)
binary.BigEndian.PutUint16(f.Payload, ws.status)
if err := ws.Send(f); err != nil {
return err
}
return ws.conn.Close()
}WebSocket Library List
Several third‑party libraries simplify WebSocket development in Go.
STDLIB (x/net/websocket)
This library is part of the standard Go distribution and implements the client and server as defined in RFC 6455. It requires no external installation but lacks some advanced features. import "golang.org/x/net/websocket" Client example:
// create connection
conn, err := websocket.Dial("{schema}://{host}:{port}", "", op.Origin)
if err != nil {
// handle error
}
defer conn.Close()
// send message
if err = websocket.JSON.Send(conn, message); err != nil {
// handle error
}
// receive message
var msg messageType
if err = websocket.JSON.Receive(conn, &msg); err != nil {
// handle error
}Server example:
mux := http.NewServeMux()
mux.Handle("/", websocket.Handler(func(conn *websocket.Conn) {
// receive and send messages
var msg messageType
if err := websocket.JSON.Receive(conn, &msg); err != nil {
// handle error
}
if err := websocket.JSON.Send(conn, msg); err != nil {
// handle error
}
}))Gorilla
The Gorilla WebSocket package provides a complete, well‑tested implementation with a stable API. go get github.com/gorilla/websocket Client example:
u := url.URL{Scheme: schema, Host: fmt.Sprintf("%s:%s", host, port), Path: "/"}
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
// handle error
}
// send
err = c.WriteMessage(websocket.TextMessage, message)
// receive
_, msg, err := c.ReadMessage()Server example:
upgrader := websocket.Upgrader{}
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
// handle error
}
// receive
messageType, msg, err := c.ReadMessage()
// send
err = c.WriteMessage(messageType, msg)Gobwas
Gobwas is a lightweight library offering zero‑copy upgrades and a low‑level API for custom packet handling. go get github.com/gobwas/ws Client example:
conn, _, _, err := ws.DefaultDialer.Dial(ctx, "{schema}://{host}:{port}")
if err != nil {
// handle error
}
err = wsutil.WriteClientMessage(conn, ws.OpText, message)
msg, _, err := wsutil.ReadServerData(conn)Server example:
listener, err := net.Listen("tcp", op.Port)
conn, err := listener.Accept()
upgrader := ws.Upgrader{}
_, err = upgrader.Upgrade(conn)
for {
reader := wsutil.NewReader(conn, ws.StateServerSide)
_, err := reader.NextFrame()
data, err := io.ReadAll(reader)
// process data ...
wsutil.WriteServerText(conn, "new server message")
}GOWebsockets
GOWebsockets offers a rich feature set, including concurrency control, compression, and custom headers. go get github.com/sacOO7/gowebsocket Client example:
socket := gowebsocket.New("{schema}://{host}:{port}")
socket.Connect()
socket.SendText(message)
socket.OnTextMessage = func(msg string, s gowebsocket.Socket) {
// handle received message
}Server example:
// Server implementation depends on the chosen HTTP framework; use the library's API to upgrade connections and read/write messages.nhooyr.io/websocket
This library is also widely used; it is discussed in detail in the book “Go Programming Journey” by the author of the referenced article.
Comparison of Existing Solutions
Below is a table comparing the four most popular Go WebSocket libraries.
Benchmark results show that Gobwas has a clear advantage in allocation count, memory usage, and execution time. Gorilla is a solid alternative with similar features. The standard library performs adequately for prototypes but lags behind in production scenarios. GOWebsockets is comparable to the standard library and suitable for quick MVPs.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Ops Development Stories
Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
