Ciclo de vida de la conexión y reconexión
Una conexión Realtime es un solo WebSocket. Cambios de red, dispositivos en reposo y reinicios de infraestructura pueden cerrarla. El cliente se reconecta automáticamente, y tu app puede mostrar el estado actual de la conexión.
Los seis estados
bird.connection.state en el cliente del navegador, y bird.connectionState en Swift y Kotlin, siempre es uno de estos (Kotlin los representa como constantes ConnectionState, así que unavailable se lee ConnectionState.UNAVAILABLE):
| Estado | Significado |
|---|---|
| initialized | El cliente existe y aún no ha abierto un socket. |
| connecting | Se está abriendo un socket, o está abierto esperando el handshake. |
| connected | El handshake se completó. La conexión tiene un ID y puede suscribirse. |
| unavailable | La conexión se cayó y hay una reconexión programada. |
| disconnected | Llamaste a disconnect(). No hay nada programado. |
| failed | El servidor rechazó esta conexión. El cliente no reintenta. |
Vincula state_change para ver cada transición, o vincula un solo nombre de estado cuando solo te interesa uno:
import { BirdRealtime } from "@messagebird/realtime";
const bird = new BirdRealtime({ appKey: "your-app-key", region: "us1" });
bird.connection.bind("state_change", ({ previous, current }) => {
console.log(previous, "->", current);
});
bird.connection.bind("unavailable", () => showReconnectingBanner());
bird.connection.bind("connected", () => hideReconnectingBanner());import BirdRealtime
let bird = BirdRealtime(options: .init(appKey: "your-app-key", region: "us1"))
bird.onConnectionStateChange { previous, current in
print(previous, "->", current)
if current == .unavailable { showReconnectingBanner() }
if current == .connected { hideReconnectingBanner() }
}import com.bird.realtime.BirdRealtime
import com.bird.realtime.BirdRealtimeOptions
import com.bird.realtime.ConnectionState
val bird = BirdRealtime(BirdRealtimeOptions(appKey = "your-app-key", region = "us1"))
bird.onConnectionStateChange { previous, current ->
println("$previous -> $current")
if (current == ConnectionState.UNAVAILABLE) showReconnectingBanner()
if (current == ConnectionState.CONNECTED) hideReconnectingBanner()
}Swift y Kotlin usan un solo observador para cada transición, así que evalúa según current. Los handlers se ejecutan en la cola principal en Swift y en el main looper de Android en Kotlin cuando está disponible. Pasa un deliveryQueue o Executor para usar otro contexto de ejecución.
Un socket abierto permanece en connecting hasta que llega el handshake del servidor. El handshake establece bird.connection.connectionId antes de que el estado pase a connected, para que la autorización pueda firmar ese ID. Si el handshake omite el ID, el cliente reporta un error y cierra el socket.
Reconexión automática
Tras una caída inesperada, el cliente espera antes de abrir un nuevo socket. El retardo usa backoff exponencial con jitter completo, una base de un segundo y un tope de 30 segundos. Cada handshake exitoso reinicia el backoff.
El código de cierre decide cuál de tres cosas ocurre:
| Código de cierre | Comportamiento |
|---|---|
| 4000 a 4099 | Rechazado. Sin reintento, el estado pasa a failed. |
| 4200 a 4299 | Reintento inmediato. |
| Todo lo demás | El estado pasa a unavailable y luego reintenta con backoff. |
Los fallos de red siguen la ruta de backoff. Maneja los códigos de rechazo en tu UI porque estos fallos terminales no se reconectan automáticamente.
La verificación de conexión inactiva
Un socket puede parecer abierto después de que su ruta de red falle. El cliente envía un ping tras el tiempo de inactividad, que por defecto es de 120 segundos a menos que el handshake proporcione otro valor. Si no llega un pong dentro del tiempo de espera de pong predeterminado de 30 segundos, el cliente cierra el socket con un código de reintento inmediato.
Qué ocurre con tus canales
Las suscripciones pertenecen a la conexión, así que una caída las elimina. El cliente conserva cada objeto de canal, se resuscribe tras el siguiente estado connected y mantiene sus handlers existentes.
El cliente llama a tu authEndpoint de nuevo para cada canal privado y de presencia usando el nuevo ID de conexión. No puede reutilizar la firma anterior porque esa firma incluye el ID de conexión previo. Tras una llamada a signin(), el cliente también inicia sesión en cada nueva conexión y reporta fallos posteriores a través de signin_error.
bird.connection.bind("signin_error", ({ message }) => {
console.warn("connection has no identity:", message);
});bird.onSigninError { error in
print("connection has no identity:", error.message)
}bird.onSigninError { error ->
println("connection has no identity: ${error.message}")
}Mientras un canal se está reconectando, channel.trigger devuelve false sin enviar el evento del cliente. Comprueba channel.subscribed, o envía después de bird:subscription_succeeded, que se dispara tras cada resuscripción exitosa.
const room = bird.subscribe("presence-room-1");
room.bind("bird:subscription_succeeded", ({ members }) => {
renderMembers(members);
});guard let room = bird.subscribe("presence-room-1") as? PresenceChannel else { return }
room.bind(BirdProtocol.Event.subscriptionSucceeded) { _ in
renderMembers(room.members)
}val room = bird.subscribe("presence-room-1")
if (room is PresenceChannel) {
room.bind(BirdProtocol.Event.SUBSCRIPTION_SUCCEEDED) {
renderMembers(room.members)
}
}Realtime no reproduce eventos publicados mientras un cliente está desconectado. Si una vista necesita recuperarse tras un intervalo, obtén su estado actual en bird:subscription_succeeded y aplica los eventos posteriores desde ahí.
Desconectar a propósito
bird.disconnect() cierra el socket sin programar una reconexión. Úsalo cuando un cliente que cerró sesión o está inactivo ya no necesita datos en tiempo real. El cliente conserva los canales, así que un bird.connect() posterior reabre el socket y se resuscribe.
bird.disconnect(); // state: disconnected, no reconnect
bird.connect(); // reopens and re-subscribesbird.disconnect() // state: disconnected, no reconnect
bird.connect() // reopens and re-subscribesbird.disconnect() // state: DISCONNECTED, no reconnect
bird.connect() // reopens and re-subscribesEn móvil, desconecta cuando la vista o actividad relevante se detenga y reconecta cuando se reanude. Los objetos de canal y las vinculaciones siguen disponibles.
Errores en la conexión frente a errores en un canal
Los errores del servidor que pertenecen a un canal llegan a ese canal. Una suscripción rechazada es el caso habitual, y un autorizador que devuelve 403 aparece aquí:
room.bind("bird:subscription_error", (err) => {
console.warn("could not join:", err);
});room.bind(BirdProtocol.Event.subscriptionError) { error in
print("could not join:", error ?? "")
}room.bind(BirdProtocol.Event.SUBSCRIPTION_ERROR) { error ->
println("could not join: $error")
}Todo lo que el cable no atribuye a un canal llega a la conexión en su lugar:
bird.connection.bind("error", ({ code, message }) => {
console.warn(code, message);
});bird.onError { error in
print(error.code ?? 0, error.message)
}bird.onError { error ->
println("${error.code} ${error.message}")
}Manejar el código de cierre 4009
El código 4009 está en la banda sin reintento, así que la conexión entra en failed. Revisa la razón para distinguir estas situaciones:
- Un miembro desconectado. Tu backend llamó al API de desconexión para este miembro, así que cada conexión que tenía fue cerrada. Consulta Desconectar conexiones de miembros.
- Una conexión que nunca se autorizó. La app requiere conexiones autorizadas y esta no se autorizó a tiempo. Consulta Requerir conexiones autorizadas.
El cliente no se reconecta automáticamente, así que tu app decide qué ocurre después:
bird.connection.bind("error", ({ code }) => {
if (code === 4009) showSignedOutScreen();
});bird.onError { error in
if error.code == 4009 { showSignedOutScreen() }
}bird.onError { error ->
if (error.code == 4009) showSignedOutScreen()
}Un estado de sesión cerrada es apropiado cuando la autenticación ha terminado. Llamar a bird.connect() inicia una nueva conexión y repite la autorización, así que reconecta solo después de que cambie el estado de autorización del usuario.
Próximos pasos
- Autorizar canales es el contrato que tu backend implementa y la solicitud que el cliente repite en cada reconexión.
- Requerir conexiones autorizadas convierte una conexión no autorizada en una cerrada.
- Desconectar conexiones de miembros es cómo una conexión se cierra con 4009 a propósito.
- Visión general de Realtime cubre canales, miembros y conexiones como modelo.
Recursos relacionados
Continúa con la documentación, guías y ejemplos sobre este tema. Los recursos están en inglés.
Explorar la funcionalidadRealtimeSeguir la ruta de aprendizajeBuild your first integrationGuía de implementaciónSend your first realtime event
Prueba el ejercicio y obtén un resumen de implementación