Saltar a contenido

Namespace API Reference

El tipo Namespace representa una conexión a un namespace específico en Socket.IO. Los namespaces permiten multiplexar múltiples conexiones sobre una única conexión WebSocket.

Tabla de Contenidos

Concepto de Namespaces

Los namespaces son canales de comunicación separados que comparten la misma conexión física. Permiten:

  • Separación lógica: Diferentes áreas de tu aplicación pueden usar diferentes namespaces
  • Eficiencia: Una sola conexión WebSocket maneja múltiples namespaces
  • Seguridad: Puedes controlar acceso a namespaces específicos

Namespace por Defecto

El namespace por defecto es /. Cuando usas métodos directamente en Socket, estás usando este namespace:

// Estos dos códigos son equivalentes
client.On("event", handler)
client.Of("/").On("event", handler)

Obtención de Namespaces

Los namespaces se obtienen a través del método Of() de Socket:

// Obtener namespace personalizado
chat := client.Of("/chat")
admin := client.Of("/admin")
notifications := client.Of("/notifications")

Métodos de Eventos

On

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

Registra un handler para escuchar un evento específico en este namespace.

Parámetros: - event (string): Nombre del evento - handler (EventHandler): Función que se ejecuta cuando se recibe el evento

Ejemplo:

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

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

chat.On("user-joined", func(data ...interface{}) {
  user := data[0].(map[string]interface{})
  fmt.Printf("%s se unió al chat\n", user["name"])
})

OnConnect

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

Registra un handler que se ejecuta cuando este namespace se conecta exitosamente.

Parámetros: - handler (func()): Función que se ejecuta al conectar

Ejemplo:

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

chat.OnConnect(func() {
  fmt.Println("Conectado al namespace /chat")

  // Emitir evento inicial en este namespace
  chat.Emit("join-room", "general")
})

OnDisconnect

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

Registra un handler que se ejecuta cuando este namespace se desconecta.

Parámetros: - handler (func(reason string)): Función que recibe la razón de la desconexión

Ejemplo:

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

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

Métodos de Emisión

Emit

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

Emite un evento al servidor en este namespace.

Parámetros: - event (string): Nombre del evento - data (...interface{}): Argumentos a enviar

Retorna: - error: Error si la operación falla

Ejemplo:

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

// Emitir mensaje simple
chat.Emit("message", "Hola a todos")

// Emitir datos estructurados
chat.Emit("action", map[string]interface{}{
  "type": "typing",
  "user": "juan",
})

// Múltiples argumentos
chat.Emit("chat-message", "juan", "Hola", time.Now().Unix())

EmitWithAck

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

Emite un evento y espera una respuesta (acknowledgment) del servidor en este namespace.

Parámetros: - event (string): Nombre del evento - ack (AckCallback): Función callback que recibe la respuesta - data (...interface{}): Argumentos a enviar

Retorna: - error: Error si la operación falla

Ejemplo:

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

chat.EmitWithAck("get-history", func(response ...interface{}) {
  if response == nil {
    fmt.Println("Timeout esperando historial")
    return
  }

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

EmitBinary

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

Emite un evento con datos binarios en este namespace.

Parámetros: - event (string): Nombre del evento - binaryData ([]byte): Datos binarios a enviar - data (...interface{}): Argumentos adicionales (opcionales)

Retorna: - error: Error si la operación falla

Ejemplo:

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

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

// Enviar imagen con metadata
chat.EmitBinary("image", imageData, map[string]interface{}{
  "filename": "photo.jpg",
  "from": "usuario123",
  "size": len(imageData),
})

Métodos de Estado

IsConnected

func (n *Namespace) IsConnected() bool

Retorna si este namespace está actualmente conectado.

Retorna: - bool: true si está conectado, false en caso contrario

Ejemplo:

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

if chat.IsConnected() {
  chat.Emit("ping")
} else {
  fmt.Println("Chat namespace no está conectado")
}

GetPath

func (n *Namespace) GetPath() string

Retorna el path de este namespace.

Retorna: - string: Path del namespace (ej: /chat, /admin)

Ejemplo:

chat := client.Of("/chat")
fmt.Printf("Namespace path: %s\n", chat.GetPath()) // Output: /chat

Ejemplos

Ejemplo Básico: Chat Application

package main

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

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

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

  chat.OnConnect(func() {
    fmt.Println("✅ Conectado al chat")

    // Unirse a una sala
    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 se unió al chat\n", username)
  })

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

  // Enviar mensaje
  chat.Emit("message", "juan", "Hola a todos!")

  select {}
}

Ejemplo: Múltiples Namespaces

package main

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

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

  // Namespace de chat público
  publicChat := client.Of("/chat")
  publicChat.OnConnect(func() {
    fmt.Println("✅ Chat público conectado")
  })
  publicChat.On("message", func(data ...interface{}) {
    fmt.Printf("💬 [Público] %s: %s\n", data[0], data[1])
  })

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

  // Namespace de notificaciones
  notifications := client.Of("/notifications")
  notifications.OnConnect(func() {
    fmt.Println("✅ Notificaciones conectadas")
  })
  notifications.On("new", func(data ...interface{}) {
    notif := data[0].(map[string]interface{})
    fmt.Printf("🔔 Notificación: %s\n", notif["message"])
  })

  // Namespace de admin (con autenticación específica)
  admin := client.Of("/admin")
  admin.OnConnect(func() {
    fmt.Println("✅ Admin conectado")
    admin.Emit("authenticate", map[string]interface{}{
      "token": "admin-secret-token",
    })
  })
  admin.On("stats", func(data ...interface{}) {
    stats := data[0].(map[string]interface{})
    fmt.Printf("📊 Stats: %v usuarios conectados\n", stats["users"])
  })

  select {}
}

Ejemplo: Namespace con 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 conectado")

    // Obtener datos de usuario
    api.EmitWithAck("get-user", func(response ...interface{}) {
      if response == nil {
        fmt.Println("⏱️  Timeout esperando usuario")
        return
      }

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

    // Crear recurso
    api.EmitWithAck("create-post", func(response ...interface{}) {
      if response == nil {
        fmt.Println("⏱️  Timeout creando post")
        return
      }

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

  select {}
}

Ejemplo: Namespace con Datos Binarios

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 conectado")

    // Subir archivo
    uploadFile(files, "document.pdf")
  })

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

  files.On("upload-complete", func(data ...interface{}) {
    result := data[0].(map[string]interface{})
    fmt.Printf("✅ Archivo subido: %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("📥 Archivo recibido: %s (%d bytes)\n", filename, len(binaryData))

    // Guardar archivo
    os.WriteFile(filename, binaryData, 0644)
  })

  select {}
}

func uploadFile(ns *socketio.Namespace, path string) {
  data, err := os.ReadFile(path)
  if err != nil {
    fmt.Printf("Error leyendo archivo: %v\n", err)
    return
  }

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

Ejemplo: Manejo de Errores por 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 que puede requerir autenticación especial
  admin := client.Of("/admin")

  admin.OnConnect(func() {
    fmt.Println("✅ Intentando autenticar en /admin")
    admin.Emit("auth", map[string]interface{}{
      "token": "admin-token",
    })
  })

  admin.On("auth-success", func(data ...interface{}) {
    fmt.Println("✅ Autenticación exitosa en /admin")
    admin.Emit("get-stats")
  })

  admin.On("auth-failed", func(data ...interface{}) {
    reason := data[0].(string)
    fmt.Printf("❌ Autenticación fallida: %s\n", reason)
    admin.Emit("disconnect") // Desconectar este namespace
  })

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

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

  select {}
}

Thread-Safety

Todos los métodos de Namespace son thread-safe y pueden ser llamados desde múltiples goroutines.

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

// Emitir desde múltiples goroutines
for i := 0; i < 10; i++ {
  go func(id int) {
    chat.Emit("worker-message", map[string]interface{}{
      "worker": id,
      "data": processData(id),
    })
  }(i)
}

Mejores Prácticas

1. Organizar por Funcionalidad

// Separar namespaces por funcionalidad
chat := client.Of("/chat")
notifications := client.Of("/notifications")
admin := client.Of("/admin")

2. Verificar Conexión Antes de Emitir

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

if chat.IsConnected() {
  chat.Emit("message", "Hola")
} else {
  fmt.Println("Chat no conectado, guardando mensaje para después")
  queueMessage("Hola")
}

3. Manejar Eventos de Conexión

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

api.OnConnect(func() {
  // Suscribirse a eventos cuando se conecta
  api.Emit("subscribe", []string{"users", "posts", "comments"})
})

api.OnDisconnect(func(reason string) {
  // Limpiar estado cuando se desconecta
  clearLocalCache()
})

4. Usar Acknowledgments para Operaciones Críticas

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

api.EmitWithAck("important-operation", func(response ...interface{}) {
  if response == nil {
    fmt.Println("Operación falló (timeout)")
    retryOperation()
    return
  }

  if response[0].(map[string]interface{})["success"].(bool) {
    fmt.Println("Operación exitosa")
  }
}, operationData)

Ver También