Zum Inhalt

Event-System

Socket.IO basiert auf einem bidirektionalen Event-System. Dieses Dokument erklärt alle verfügbaren Events und deren Verwendung.

Inhaltsverzeichnis

Lifecycle-Events

Diese Events werden automatisch während des Verbindungslebenszyklus emittiert.

OnConnect

Wird ausgeführt, wenn die Verbindung erfolgreich hergestellt wurde.

client.OnConnect(func() {
  fmt.Println("Conectado al servidor")

  // Aquí puedes emitir eventos iniciales
  client.Emit("join-room", "sala-123")
})

OnDisconnect

Wird ausgeführt, wenn die Verbindung geschlossen wird. Erhält den Grund für die Trennung.

client.OnDisconnect(func(reason string) {
  fmt.Printf("Desconectado: %s\n", reason)

  // Razones comunes:
  // - "io server disconnect" - El servidor cerró la conexión
  // - "io client disconnect" - El cliente cerró la conexión
  // - "ping timeout" - El servidor no respondió al ping
  // - "transport close" - El transporte subyacente se cerró
  // - "transport error" - Error en el transporte
})

OnError

Wird ausgeführt, wenn ein Verbindungsfehler auftritt.

client.OnError(func(err error) {
  fmt.Printf("Error de conexión: %v\n", err)

  // Puedes implementar lógica de recuperación aquí
  if err.Error() == "authentication failed" {
    fmt.Println("Verifica tus credenciales")
  }
})

Reconnection-Events

Diese Events ermöglichen die Überwachung und Steuerung des automatischen Reconnection-Prozesses.

OnReconnectAttempt

Wird vor jedem Reconnection-Versuch ausgeführt.

client.OnReconnectAttempt(func(attempt int) {
  fmt.Printf("Intentando reconectar... (intento %d)\n", attempt)
})

OnReconnect

Wird ausgeführt, wenn die Reconnection erfolgreich ist.

client.OnReconnect(func(attempt int) {
  fmt.Printf("Reconectado exitosamente después de %d intentos\n", attempt)

  // Re-subscribirse a eventos o actualizar estado
  client.Emit("resume-session", lastSessionId)
})

OnReconnectError

Wird ausgeführt, wenn ein Reconnection-Versuch fehlschlägt.

client.OnReconnectError(func(err error) {
  fmt.Printf("Error en reconexión: %v\n", err)
})

OnReconnectFailed

Wird ausgeführt, wenn alle Reconnection-Versuche fehlschlagen.

client.OnReconnectFailed(func() {
  fmt.Println("Reconexión falló después de todos los intentos")

  // Notificar al usuario o implementar fallback
  notifyUser("No se pudo restablecer la conexión")
})

Vollständiges Reconnection-Beispiel

client := socketio.New("ws://localhost:3000", socketio.Options{
  ReconnectAttempts: 5,
  ReconnectDelay:    time.Second,
  ReconnectDelayMax: 5 * time.Second,
})

var reconnectCount int

client.OnReconnectAttempt(func(attempt int) {
  reconnectCount = attempt
  fmt.Printf("⏳ Intento %d de reconexión...\n", attempt)
})

client.OnReconnect(func(attempt int) {
  fmt.Printf("✅ Reconectado después de %d intentos\n", attempt)
  reconnectCount = 0
})

client.OnReconnectError(func(err error) {
  fmt.Printf("❌ Error en intento %d: %v\n", reconnectCount, err)
})

client.OnReconnectFailed(func() {
  fmt.Println("💥 Reconexión falló permanentemente")
})

Custom Events

Sie können beliebige vom Server emittierte Custom Events empfangen.

Events empfangen

client.On("message", func(data ...interface{}) {
  fmt.Printf("Mensaje recibido: %v\n", data[0])
})

client.On("user-joined", func(data ...interface{}) {
  username := data[0].(string)
  fmt.Printf("%s se unió a la sala\n", username)
})

client.On("notification", func(data ...interface{}) {
  notification := data[0].(map[string]interface{})
  fmt.Printf("Notificación: %s\n", notification["message"])
})

Mehrere Argumente

Events können mehrere Argumente empfangen:

client.On("chat-message", func(data ...interface{}) {
  username := data[0].(string)
  message := data[1].(string)
  timestamp := data[2].(float64)

  fmt.Printf("[%s] %s: %s\n",
    time.Unix(int64(timestamp), 0).Format("15:04"),
    username,
    message)
})

Namespaces

Events können in bestimmten Namespaces empfangen werden:

// Namespace por defecto
client.On("global-event", func(data ...interface{}) {
  fmt.Println("Evento global:", data[0])
})

// Namespace personalizado
chat := client.Of("/chat")
chat.On("message", func(data ...interface{}) {
  fmt.Println("Mensaje de chat:", data[0])
})

admin := client.Of("/admin")
admin.On("alert", func(data ...interface{}) {
  fmt.Println("Alerta de admin:", data[0])
})

Event Handlers

Handler-Signatur

Event Handlers haben folgende Signatur:

type EventHandler func(data ...interface{})

Der Parameter data enthält alle mit dem Event gesendeten Argumente.

Type Assertions

Sie müssen Type Assertions durchführen, um die empfangenen Daten zu verwenden:

client.On("user-data", func(data ...interface{}) {
  if len(data) == 0 {
    return
  }

  // Type assertion a map
  if userData, ok := data[0].(map[string]interface{}); ok {
    name := userData["name"].(string)
    age := userData["age"].(float64)
    fmt.Printf("Usuario: %s, Edad: %.0f\n", name, age)
  }
})

Fehlerbehandlung in Handlers

client.On("data", func(data ...interface{}) {
  defer func() {
    if r := recover(); r != nil {
      fmt.Printf("Error en handler: %v\n", r)
    }
  }()

  // Procesar datos
  processData(data[0])
})

Acknowledgments

Acknowledgments ermöglichen bidirektionale Antworten.

Mit Acknowledgment senden

client.EmitWithAck("get-user", func(response ...interface{}) {
  user := response[0].(map[string]interface{})
  fmt.Printf("Usuario obtenido: %v\n", user)
}, "user-123")

Auf Acknowledgments antworten

client.On("save-data", func(data ...interface{}) {
  // Último argumento es el callback de acknowledgment
  if len(data) < 2 {
    return
  }

  payload := data[0]

  if ack, ok := data[len(data)-1].(func(...interface{})); ok {
    // Procesar datos
    success := saveToDatabase(payload)

    // Responder al servidor
    if success {
      ack(map[string]interface{}{
        "status": "ok",
        "message": "Datos guardados exitosamente",
      })
    } else {
      ack(map[string]interface{}{
        "status": "error",
        "message": "Error al guardar datos",
      })
    }
  }
})

Acknowledgment-Timeout

Sie können ein globales Timeout für Acknowledgments konfigurieren:

client := socketio.New("ws://localhost:3000", socketio.Options{
  AckTimeout: 5 * time.Second, // Timeout de 5 segundos
})

client.EmitWithAck("slow-operation", func(response ...interface{}) {
  if response == nil {
    fmt.Println("Timeout: el servidor no respondió a tiempo")
    return
  }
  fmt.Println("Respuesta:", response[0])
}, "datos")

Binäre Events

Um binäre Daten zu senden (Bilder, Dateien, etc.):

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

// Emitir evento binario
client.Of("/").EmitBinary("upload", imageData, map[string]interface{}{
  "filename": "photo.jpg",
  "size": len(imageData),
})

Binäre Daten empfangen

client.On("file", func(data ...interface{}) {
  // Primer argumento es el buffer binario
  if binaryData, ok := data[0].([]byte); ok {
    fmt.Printf("Archivo recibido: %d bytes\n", len(binaryData))

    // Guardar archivo
    err := os.WriteFile("downloaded.dat", binaryData, 0644)
    if err != nil {
      fmt.Println("Error al guardar archivo:", err)
    }
  }

  // Argumentos adicionales
  if len(data) > 1 {
    metadata := data[1].(map[string]interface{})
    fmt.Printf("Metadata: %v\n", metadata)
  }
})

Best Practices

1. Empfangene Daten validieren

Validieren Sie Daten immer vor der Verwendung:

client.On("update", func(data ...interface{}) {
  if len(data) == 0 {
    fmt.Println("Evento sin datos")
    return
  }

  payload, ok := data[0].(map[string]interface{})
  if !ok {
    fmt.Println("Formato de datos inválido")
    return
  }

  // Usar datos validados
  processUpdate(payload)
})

2. Goroutines für lange Operationen verwenden

Blockieren Sie den Event Handler nicht mit langen Operationen:

client.On("process", func(data ...interface{}) {
  go func() {
    // Operación larga
    result := heavyComputation(data[0])

    // Emitir resultado
    client.Emit("result", result)
  }()
})

3. Ressourcen aufräumen

Deregistrieren Sie Handlers, wenn Sie sie nicht mehr benötigen:

// El cliente no proporciona Off() directamente
// En su lugar, usa flags o canales para controlar la ejecución

done := make(chan struct{})

client.On("temp-event", func(data ...interface{}) {
  select {
  case <-done:
    return // Ignorar evento
  default:
    processEvent(data)
  }
})

// Cuando termines
close(done)

4. Strukturiertes Logging

Implementieren Sie konsistentes Logging:

func logEvent(event string, data ...interface{}) {
  fmt.Printf("[%s] Evento: %s, Datos: %v\n",
    time.Now().Format("15:04:05"),
    event,
    data)
}

client.On("important-event", func(data ...interface{}) {
  logEvent("important-event", data...)
  processEvent(data)
})

Siehe auch