Sistema de Eventos¶
Socket.IO se basa en un sistema de eventos bidireccional. Este documento explica todos los eventos disponibles y cómo usarlos.
Tabla de Contenidos¶
- Eventos del Ciclo de Vida
- Eventos de Reconexión
- Eventos Personalizados
- Event Handlers
- Acknowledgments
- Eventos Binarios
Eventos del Ciclo de Vida¶
Estos eventos se emiten automáticamente durante el ciclo de vida de la conexión.
OnConnect¶
Se ejecuta cuando la conexión se establece exitosamente.
client.OnConnect(func() {
fmt.Println("Conectado al servidor")
// Aquí puedes emitir eventos iniciales
client.Emit("join-room", "sala-123")
})
OnDisconnect¶
Se ejecuta cuando la conexión se cierra. Recibe la razón de la desconexión.
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¶
Se ejecuta cuando ocurre un error en la conexión.
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")
}
})
Eventos de Reconexión¶
Estos eventos te permiten monitorear y controlar el proceso de reconexión automática.
OnReconnectAttempt¶
Se ejecuta antes de cada intento de reconexión.
client.OnReconnectAttempt(func(attempt int) {
fmt.Printf("Intentando reconectar... (intento %d)\n", attempt)
})
OnReconnect¶
Se ejecuta cuando la reconexión es exitosa.
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¶
Se ejecuta cuando un intento de reconexión falla.
OnReconnectFailed¶
Se ejecuta cuando todos los intentos de reconexión fallan.
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")
})
Ejemplo Completo de Reconexión¶
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")
})
Eventos Personalizados¶
Puedes escuchar cualquier evento personalizado emitido por el servidor.
Escuchar Eventos¶
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"])
})
Múltiples Argumentos¶
Los eventos pueden recibir múltiples argumentos:
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¶
Los eventos se pueden escuchar en namespaces específicos:
// 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¶
Firma del Handler¶
Los event handlers tienen la siguiente firma:
El parámetro data contiene todos los argumentos enviados con el evento.
Type Assertions¶
Debes hacer type assertions para usar los datos recibidos:
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)
}
})
Manejo de Errores en 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¶
Los acknowledgments permiten respuestas bidireccionales.
Emitir con Acknowledgment¶
client.EmitWithAck("get-user", func(response ...interface{}) {
user := response[0].(map[string]interface{})
fmt.Printf("Usuario obtenido: %v\n", user)
}, "user-123")
Responder a Acknowledgments¶
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",
})
}
}
})
Timeout de Acknowledgments¶
Puedes configurar un timeout global para acknowledgments:
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")
Eventos Binarios¶
Para enviar datos binarios (imágenes, archivos, 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),
})
Recibir Datos Binarios¶
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)
}
})
Mejores Prácticas¶
1. Validar Datos Recibidos¶
Siempre valida los datos antes de usarlos:
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. Usar Goroutines para Operaciones Largas¶
No bloquees el event handler con operaciones largas:
client.On("process", func(data ...interface{}) {
go func() {
// Operación larga
result := heavyComputation(data[0])
// Emitir resultado
client.Emit("result", result)
}()
})
3. Limpiar Recursos¶
Desregistra handlers cuando ya no los necesites:
// 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. Logging Estructurado¶
Implementa logging consistente:
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)
})