Zum Inhalt

Namespace API Reference

Der Typ Namespace repräsentiert eine Verbindung zu einem bestimmten Namespace in Socket.IO. Namespaces ermöglichen das Multiplexen mehrerer Verbindungen über eine einzelne WebSocket-Verbindung.

Inhaltsverzeichnis

Konzept der Namespaces

Namespaces sind separate Kommunikationskanäle, die dieselbe physische Verbindung teilen. Sie ermöglichen:

  • Logische Trennung: Verschiedene Bereiche Ihrer Anwendung können verschiedene Namespaces verwenden
  • Effizienz: Eine einzelne WebSocket-Verbindung verarbeitet mehrere Namespaces
  • Sicherheit: Sie können den Zugriff auf bestimmte Namespaces steuern

Standard-Namespace

Der Standard-Namespace ist /. Wenn Sie Methoden direkt auf Socket verwenden, verwenden Sie diesen Namespace:

// Diese beiden Codes sind äquivalent
client.On("event", handler)
client.Of("/").On("event", handler)

Abrufen von Namespaces

Namespaces werden über die Methode Of() von Socket abgerufen:

// Benutzerdefinierten Namespace abrufen
chat := client.Of("/chat")
admin := client.Of("/admin")
notifications := client.Of("/notifications")

Ereignismethoden

On

func (n *Namespace) On(event string, handler EventHandler)

Registriert einen Handler zum Abhören eines bestimmten Ereignisses in diesem Namespace.

Parameter: - event (string): Ereignisname - handler (EventHandler): Funktion, die beim Empfang des Ereignisses ausgeführt wird

Beispiel:

chat := client.Of("/chat")

chat.On("message", func(data ...interface{}) {
  msg := data[0].(string)
  fmt.Printf("Chat-Nachricht: %s\n", msg)
})

chat.On("user-joined", func(data ...interface{}) {
  user := data[0].(map[string]interface{})
  fmt.Printf("%s ist dem Chat beigetreten\n", user["name"])
})

OnConnect

func (n *Namespace) OnConnect(handler func())

Registriert einen Handler, der ausgeführt wird, wenn dieser Namespace erfolgreich verbunden ist.

Parameter: - handler (func()): Funktion, die beim Verbinden ausgeführt wird

Beispiel:

chat := client.Of("/chat")

chat.OnConnect(func() {
  fmt.Println("Mit Namespace /chat verbunden")

  // Initiales Ereignis in diesem Namespace senden
  chat.Emit("join-room", "general")
})

OnDisconnect

func (n *Namespace) OnDisconnect(handler func(reason string))

Registriert einen Handler, der ausgeführt wird, wenn dieser Namespace getrennt wird.

Parameter: - handler (func(reason string)): Funktion, die den Grund für die Trennung erhält

Beispiel:

chat := client.Of("/chat")

chat.OnDisconnect(func(reason string) {
  fmt.Printf("Von /chat getrennt: %s\n", reason)
})

Sendemethoden

Emit

func (n *Namespace) Emit(event string, data ...interface{}) error

Sendet ein Ereignis an den Server in diesem Namespace.

Parameter: - event (string): Ereignisname - data (...interface{}): Zu sendende Argumente

Rückgabewert: - error: Fehler, falls die Operation fehlschlägt

Beispiel:

chat := client.Of("/chat")

// Einfache Nachricht senden
chat.Emit("message", "Hallo an alle")

// Strukturierte Daten senden
chat.Emit("action", map[string]interface{}{
  "type": "typing",
  "user": "juan",
})

// Mehrere Argumente
chat.Emit("chat-message", "juan", "Hallo", time.Now().Unix())

EmitWithAck

func (n *Namespace) EmitWithAck(event string, ack AckCallback, data ...interface{}) error

Sendet ein Ereignis und wartet auf eine Antwort (Acknowledgment) vom Server in diesem Namespace.

Parameter: - event (string): Ereignisname - ack (AckCallback): Callback-Funktion, die die Antwort erhält - data (...interface{}): Zu sendende Argumente

Rückgabewert: - error: Fehler, falls die Operation fehlschlägt

Beispiel:

chat := client.Of("/chat")

chat.EmitWithAck("get-history", func(response ...interface{}) {
  if response == nil {
    fmt.Println("Timeout beim Warten auf Verlauf")
    return
  }

  history := response[0].([]interface{})
  fmt.Printf("Verlauf erhalten: %d Nachrichten\n", len(history))
}, "sala-general")

EmitBinary

func (n *Namespace) EmitBinary(event string, binaryData []byte, data ...interface{}) error

Sendet ein Ereignis mit Binärdaten in diesem Namespace.

Parameter: - event (string): Ereignisname - binaryData ([]byte): Zu sendende Binärdaten - data (...interface{}): Zusätzliche Argumente (optional)

Rückgabewert: - error: Fehler, falls die Operation fehlschlägt

Beispiel:

chat := client.Of("/chat")

// Bild lesen
imageData, err := os.ReadFile("photo.jpg")
if err != nil {
  log.Fatal(err)
}

// Bild mit Metadaten senden
chat.EmitBinary("image", imageData, map[string]interface{}{
  "filename": "photo.jpg",
  "from": "usuario123",
  "size": len(imageData),
})

Statusmethoden

IsConnected

func (n *Namespace) IsConnected() bool

Gibt zurück, ob dieser Namespace derzeit verbunden ist.

Rückgabewert: - bool: true wenn verbunden, false andernfalls

Beispiel:

chat := client.Of("/chat")

if chat.IsConnected() {
  chat.Emit("ping")
} else {
  fmt.Println("Chat-Namespace ist nicht verbunden")
}

GetPath

func (n *Namespace) GetPath() string

Gibt den Pfad dieses Namespace zurück.

Rückgabewert: - string: Namespace-Pfad (z.B.: /chat, /admin)

Beispiel:

chat := client.Of("/chat")
fmt.Printf("Namespace-Pfad: %s\n", chat.GetPath()) // Ausgabe: /chat

Beispiele

Grundlegendes Beispiel: Chat-Anwendung

package main

import (
  "fmt"
  socketio "github.com/arcaela/socket.io-client-go"
)

func main() {
  client := socketio.New("ws://localhost:3000")
  defer client.Close()

  // Chat-Namespace
  chat := client.Of("/chat")

  chat.OnConnect(func() {
    fmt.Println("✅ Mit Chat verbunden")

    // Einem Raum beitreten
    chat.Emit("join-room", "general")
  })

  chat.On("message", func(data ...interface{}) {
    username := data[0].(string)
    message := data[1].(string)
    fmt.Printf("💬 [%s]: %s\n", username, message)
  })

  chat.On("user-joined", func(data ...interface{}) {
    username := data[0].(string)
    fmt.Printf("👤 %s ist dem Chat beigetreten\n", username)
  })

  chat.On("user-left", func(data ...interface{}) {
    username := data[0].(string)
    fmt.Printf("👋 %s hat den Chat verlassen\n", username)
  })

  // Nachricht senden
  chat.Emit("message", "juan", "Hallo an alle!")

  select {}
}

Beispiel: Mehrere Namespaces

package main

import (
  "fmt"
  socketio "github.com/arcaela/socket.io-client-go"
)

func main() {
  client := socketio.New("ws://localhost:3000")
  defer client.Close()

  // Öffentlicher Chat-Namespace
  publicChat := client.Of("/chat")
  publicChat.OnConnect(func() {
    fmt.Println("✅ Öffentlicher Chat verbunden")
  })
  publicChat.On("message", func(data ...interface{}) {
    fmt.Printf("💬 [Öffentlich] %s: %s\n", data[0], data[1])
  })

  // Privater Chat-Namespace
  privateChat := client.Of("/chat/private")
  privateChat.OnConnect(func() {
    fmt.Println("✅ Privater Chat verbunden")
  })
  privateChat.On("message", func(data ...interface{}) {
    fmt.Printf("🔒 [Privat] %s: %s\n", data[0], data[1])
  })

  // Benachrichtigungs-Namespace
  notifications := client.Of("/notifications")
  notifications.OnConnect(func() {
    fmt.Println("✅ Benachrichtigungen verbunden")
  })
  notifications.On("new", func(data ...interface{}) {
    notif := data[0].(map[string]interface{})
    fmt.Printf("🔔 Benachrichtigung: %s\n", notif["message"])
  })

  // Admin-Namespace (mit spezifischer Authentifizierung)
  admin := client.Of("/admin")
  admin.OnConnect(func() {
    fmt.Println("✅ Admin verbunden")
    admin.Emit("authenticate", map[string]interface{}{
      "token": "admin-secret-token",
    })
  })
  admin.On("stats", func(data ...interface{}) {
    stats := data[0].(map[string]interface{})
    fmt.Printf("📊 Statistik: %v verbundene Benutzer\n", stats["users"])
  })

  select {}
}

Beispiel: Namespace mit Acknowledgments

package main

import (
  "fmt"
  "time"
  socketio "github.com/arcaela/socket.io-client-go"
)

func main() {
  client := socketio.New("ws://localhost:3000")
  defer client.Close()

  api := client.Of("/api")

  api.OnConnect(func() {
    fmt.Println("✅ API-Namespace verbunden")

    // Benutzerdaten abrufen
    api.EmitWithAck("get-user", func(response ...interface{}) {
      if response == nil {
        fmt.Println("⏱️  Timeout beim Warten auf Benutzer")
        return
      }

      user := response[0].(map[string]interface{})
      fmt.Printf("👤 Benutzer: %s\n", user["name"])
    }, "user-123")

    // Ressource erstellen
    api.EmitWithAck("create-post", func(response ...interface{}) {
      if response == nil {
        fmt.Println("⏱️  Timeout beim Erstellen des Beitrags")
        return
      }

      result := response[0].(map[string]interface{})
      if result["success"].(bool) {
        fmt.Printf("✅ Beitrag erstellt mit ID: %v\n", result["id"])
      } else {
        fmt.Printf("❌ Fehler: %s\n", result["error"])
      }
    }, map[string]interface{}{
      "title": "Mi primer post",
      "content": "Contenido del post",
    })
  })

  select {}
}

Beispiel: Namespace mit Binärdaten

package main

import (
  "fmt"
  "os"
  socketio "github.com/arcaela/socket.io-client-go"
)

func main() {
  client := socketio.New("ws://localhost:3000")
  defer client.Close()

  files := client.Of("/files")

  files.OnConnect(func() {
    fmt.Println("✅ Files-Namespace verbunden")

    // Datei hochladen
    uploadFile(files, "document.pdf")
  })

  files.On("upload-progress", func(data ...interface{}) {
    progress := data[0].(map[string]interface{})
    fmt.Printf("📤 Fortschritt: %.0f%%\n", progress["percent"])
  })

  files.On("upload-complete", func(data ...interface{}) {
    result := data[0].(map[string]interface{})
    fmt.Printf("✅ Datei hochgeladen: %s\n", result["url"])
  })

  files.On("file-received", func(data ...interface{}) {
    binaryData := data[0].([]byte)
    metadata := data[1].(map[string]interface{})

    filename := metadata["filename"].(string)
    fmt.Printf("📥 Datei empfangen: %s (%d Bytes)\n", filename, len(binaryData))

    // Datei speichern
    os.WriteFile(filename, binaryData, 0644)
  })

  select {}
}

func uploadFile(ns *socketio.Namespace, path string) {
  data, err := os.ReadFile(path)
  if err != nil {
    fmt.Printf("Fehler beim Lesen der Datei: %v\n", err)
    return
  }

  ns.EmitBinary("upload", data, map[string]interface{}{
    "filename": path,
    "size": len(data),
  })
}

Beispiel: Fehlerbehandlung nach Namespace

package main

import (
  "fmt"
  socketio "github.com/arcaela/socket.io-client-go"
)

func main() {
  client := socketio.New("ws://localhost:3000")
  defer client.Close()

  // Namespace, der spezielle Authentifizierung erfordern kann
  admin := client.Of("/admin")

  admin.OnConnect(func() {
    fmt.Println("✅ Authentifizierung in /admin wird versucht")
    admin.Emit("auth", map[string]interface{}{
      "token": "admin-token",
    })
  })

  admin.On("auth-success", func(data ...interface{}) {
    fmt.Println("✅ Authentifizierung in /admin erfolgreich")
    admin.Emit("get-stats")
  })

  admin.On("auth-failed", func(data ...interface{}) {
    reason := data[0].(string)
    fmt.Printf("❌ Authentifizierung fehlgeschlagen: %s\n", reason)
    admin.Emit("disconnect") // Diesen Namespace trennen
  })

  admin.On("error", func(data ...interface{}) {
    err := data[0].(string)
    fmt.Printf("⚠️  Fehler in /admin: %s\n", err)
  })

  admin.OnDisconnect(func(reason string) {
    fmt.Printf("❌ /admin getrennt: %s\n", reason)
  })

  select {}
}

Thread-Sicherheit

Alle Methoden von Namespace sind thread-sicher und können von mehreren Goroutinen aufgerufen werden.

chat := client.Of("/chat")

// Von mehreren Goroutinen senden
for i := 0; i < 10; i++ {
  go func(id int) {
    chat.Emit("worker-message", map[string]interface{}{
      "worker": id,
      "data": processData(id),
    })
  }(i)
}

Best Practices

1. Nach Funktionalität organisieren

// Namespaces nach Funktionalität trennen
chat := client.Of("/chat")
notifications := client.Of("/notifications")
admin := client.Of("/admin")

2. Verbindung vor dem Senden überprüfen

chat := client.Of("/chat")

if chat.IsConnected() {
  chat.Emit("message", "Hallo")
} else {
  fmt.Println("Chat nicht verbunden, Nachricht für später speichern")
  queueMessage("Hallo")
}

3. Verbindungsereignisse behandeln

api := client.Of("/api")

api.OnConnect(func() {
  // Bei Verbindung Ereignisse abonnieren
  api.Emit("subscribe", []string{"users", "posts", "comments"})
})

api.OnDisconnect(func(reason string) {
  // Status bei Trennung bereinigen
  clearLocalCache()
})

4. Acknowledgments für kritische Operationen verwenden

api := client.Of("/api")

api.EmitWithAck("important-operation", func(response ...interface{}) {
  if response == nil {
    fmt.Println("Operation fehlgeschlagen (Timeout)")
    retryOperation()
    return
  }

  if response[0].(map[string]interface{})["success"].(bool) {
    fmt.Println("Operation erfolgreich")
  }
}, operationData)

Siehe auch