Sling Academy
Home/Golang/Implementing Private Messaging in a WebSocket Chat Application in Go

Implementing Private Messaging in a WebSocket Chat Application in Go

Last updated: November 26, 2024

Creating a real-time chat application with private messaging functionality can significantly enhance the user experience, allowing users to communicate directly and securely. Go, with its native concurrency support, is a great option for building a high-performance WebSocket chat application. This article guides you through implementing a private messaging feature in a WebSocket chat app using Go.

Setting Up the WebSocket Server

First, set up a simple WebSocket server in Go. We'll be using the github.com/gorilla/websocket package to handle WebSocket connections:

package main

import (
    "net/http"
    "github.com/gorilla/websocket"
    "log"
)

var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        return true
    },
}

type Client struct {
    conn *websocket.Conn
    send chan []byte
}

func handleConnections(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Fatalf("Failed to upgrade: %v", err)
    }
    defer conn.Close()

    client := &Client{conn: conn, send: make(chan []byte)}
    // Add more setup for client, such as adding to client list...
}

Handling Incoming Messages

Clients will need to send messages through their WebSocket connection. Let's add a basic message reading loop:

func (c *Client) readMessages() {
    defer c.conn.Close()
    for {
        _, message, err := c.conn.ReadMessage()
        if err != nil {
            log.Printf("error: %v", err)
            break
        }
        // Here, process the message, for example, by broadcasting it to clients or handling private messages.
    }
}

Implementing Private Messaging

To handle private messaging, we'll use a simple protocol where a message may contain a prefix with the recipient's ID. Let's add a function to parse and deliver these messages:

func sendMessageToClient(targetID string, message []byte) {
    // Assuming we have a map of clients
    target, exists := clients[targetID]
    if exists {
        target.send <- message
    }
}

func (c *Client) handleMessage(message []byte) {
    parts := bytes.SplitN(message, []byte(" "), 2)
    if len(parts) != 2 {
        return
    }
    recipientID := string(parts[0])
    sendMessageToClient(recipientID, parts[1])
}

Broadcast vs. Private Messaging

Your application should differentiate between messages meant for everyone and those targeting specific users. A simple approach is prefixing private messages with a user identifier.

func (c *Client) broadcastOrSend(message []byte) {
    if strings.HasPrefix(string(message), "@"){ // assuming @ indicates a private message
        c.handleMessage(message)
    } else {
        // Code to broadcast message to all clients
    }
}

Complete Client Setup

The client setup completes on the WebSocket connection side, incorporating methods to send messages, be it broadcast or private:

func (c *Client) writeMessages() {
    defer c.conn.Close()
    for message := range c.send {
        err := c.conn.WriteMessage(websocket.TextMessage, message)
        if err != nil {
            log.Printf("write error: %v", err)
            break
        }
    }
}

Finally, integrate the read and write processes within the connection handling:

func handleConnections(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Fatalf("Failed to upgrade: %v", err)
    }
    defer conn.Close()

    client := &Client{conn: conn, send: make(chan []byte)}
    go client.readMessages()
    go client.writeMessages()

    // Save client to global client map...
}

Conclusion

In this article, we've discussed creating a private messaging feature using Go and the Gorilla WebSocket package. With this implementation, users can broadcast messages or send direct messages within a chat application. You'll need to manage the `clients` mapping to connect each client correctly and ensure proper handling of concurrent read/writes.

Next Article: Working with WebSocket Compression for Efficient Data Transfer in Go

Previous Article: Building a Real-Time Notifications System Using WebSockets in Go

Series: Websocket & Chat Programs in Go

Golang

Related Articles

You May Also Like

  • How to remove HTML tags in a string in Go
  • How to remove special characters in a string in Go
  • How to remove consecutive whitespace in a string in Go
  • How to count words and characters in a string in Go
  • Relative imports in Go: Tutorial & Examples
  • How to run Python code with Go
  • How to generate slug from title in Go
  • How to create an XML sitemap in Go
  • How to redirect in Go (301, 302, etc)
  • Using Go with MongoDB: CRUD example
  • Auto deploy Go apps with CI/ CD and GitHub Actions
  • Fixing Go error: method redeclared with different receiver type
  • Fixing Go error: copy argument must have slice type
  • Fixing Go error: attempted to use nil slice
  • Fixing Go error: assignment to constant variable
  • Fixing Go error: cannot compare X (type Y) with Z (type W)
  • Fixing Go error: method has pointer receiver, not called with pointer
  • Fixing Go error: assignment mismatch: X variables but Y values
  • Fixing Go error: array index must be non-negative integer constant