# Connections overview Clients establish and maintain a connection to the Ably service using the most efficient transport available, typically [WebSockets](https://ably.com/topic/websockets). ## Connection multiplexing Ably SDKs operate and multiplex all [channel](https://ably.com/docs/channels) traffic over a single connection. This means you can publish and subscribe to messages on any number of channels simultaneously using just one transport connection. This approach: * Maximizes throughput by efficiently utilizing the available connection. * Minimizes bandwidth consumption by avoiding multiple connection overhead. * Reduces power usage by maintaining fewer active connections. All Ably client libraries support multiplexing by default when using the realtime interface. You can dynamically subscribe and unsubscribe from channels at any time without needing to establish new connections. Once connected, clients can monitor and manage their [connection state](https://ably.com/docs/connect/states). ## Create a connection Ably SDKs open and maintain a connection to the Ably realtime servers on instantiation, which can be interacted with by using the `Connection` object. The lifecycle of connections are reported by different [connection states](https://ably.com/docs/connect/states#connection-states) to simplify monitoring and managing connections. This example relies on the default auto-connect behavior of the SDK, checking for when the connection state is `connected` event: ```realtime_javascript // Using callbacks const ably = new Ably.Realtime({ 'your-api-key' }); ably.connection.on('connected', () => { console.log('Connected to Ably!'); }); // Using promises const Ably = require('ably'); const ably = new Ably.Realtime('your-api-key'); await ably.connection.once('connected'); console.log('Connected to Ably!'); ``` ```realtime_nodejs // Using callbacks const Ably = require('ably'); const ably = new Ably.Realtime({ 'your-api-key' }); ably.connection.on('connected', () => { console.log('Connected to Ably!'); }); // Using promises const Ably = require('ably'); const ably = new Ably.Realtime('your-api-key'); await ably.connection.once('connected'); console.log('Connected to Ably!'); ``` ```realtime_ruby ably = Ably::Realtime.new('your-api-key') ably.connection.on(:connected) do puts "Connected to Ably!" end ``` ```realtime_python ably = AblyRealtime('your-api-key') await ably.connection.once_async('connected') print('Connected to Ably') ``` ```realtime_java AblyRealtime ably = new AblyRealtime("your-api-key"); ably.connection.on(ConnectionEvent.connected, new ConnectionStateListener() { @Override public void onConnectionStateChanged(ConnectionStateChange change) { System.out.println("Connected to Ably!"); } }); ``` ```realtime_csharp AblyRealtime ably = new AblyRealtime("your-api-key"); ably.Connection.On(ConnectionEvent.Connected, args => { Console.WriteLine("Connected to Ably!"); }); ``` ```realtime_objc ARTRealtime *ably = [[ARTRealtime alloc] initWithKey:@"your-api-key"]; [ably.connection on:ARTRealtimeConnectionEventConnected callback:^(ARTConnectionStateChange *change) { NSLog(@"Connected to Ably!"); }]; ``` ```realtime_swift let realtime = ARTRealtime(key: "your-api-key") realtime.connection.on(.connected) { change in print("Connected to Ably!") } ``` ```realtime_flutter final realtime = ably.Realtime(key: 'your-api-key'); realtime.connection .on(ably.ConnectionEvent.connected) .listen((ably.ConnectionStateChange stateChange) { print('Connected to Ably!'); } ); ``` ```realtime_go realtime, err := ably.NewRealtime(ably.WithKey("your-api-key")) if err != nil { log.Fatalf("Error creating Ably client: %v", err) } // Subscribe to the 'connected' event realtime.Connection.On(ably.ConnectionEventConnected, func(stateChange ably.ConnectionStateChange) { log.Println("Connected to Ably!") }) ``` If you're not using the SDK's auto-connect feature you can also connect with [`connect()`](https://ably.com/docs/api/realtime-sdk/connection#connect) to manually connect unless the state is already `connected` or `connecting`. Explicitly calling connect is unnecessary unless the ClientOptions attribute autoConnect is false. Unless already connected or connecting, this method causes the connection to open, entering the connecting state. To manually attempt to open a connection you call the [`connect()`](https://ably.com/docs/api/realtime-sdk/connection#connect) function: `ably.connect()`. ## Monitor connections to an app Connection monitoring allows you to view and manage the [states of connections](https://ably.com/docs/connect/states) to Ably, showing events for individual people connecting and disconnecting. The developer console in your Ably account also shows these events. This feature is intended for debugging, so once the number of new connections exceeds the number of messages per second permitted by the lifecycle channel, new events will be dropped. This means if you want a definitive list of everyone using your app you'd be best using [token authentication](https://ably.com/docs/auth/token) to create your own 'auth server'. The Ably dashboard contains a developer console. In the developer console you can view connection events. ### Connection IDs A connection ID is a unique identifier given to a connection, allowing for identifying and specifying particular connections. An active connection ID is guaranteed to be unique in the Ably system whilst it is active, i.e. no other connection will share that connection ID. However, Ably reserves the right to generate a new connection ID later that may be the same as a previously discarded connection ID (once the connection is closed). Therefore customers are advised to not use the connection ID as a perpetual unique identifier as it is possible that a connection ID may be used many times. ### Connection metachannels [Metachannels](https://ably.com/docs/metadata-stats/metadata/subscribe) are a namespace of channels beginning with the [meta] qualifier, distinguishing them from regular channels. For connections there is a specific `[meta]connection.lifecycle` channel that publishes messages about the lifecycle of realtime connections. The connection lifecycle consists of a number of [connection states](https://ably.com/docs/connect/states#available-connection-states) that can be observed and interacted with using methods available on the connection object. ## Heartbeats Heartbeats enable Ably to identify clients that abruptly disconnect from the service, such as where an internet connection drops out or a client changes networks. Ably sends a heartbeat to connected clients every 15 seconds. If a client goes more than 25 seconds without seeing any server activity from Ably, it assumes that something has gone wrong with the connection and the [connection state](https://ably.com/docs/connect/states) will become `disconnected`. The 25 seconds the client waits is the heartbeat interval plus a 10 second margin of error to allow for network delays. Ably also uses this mechanism to detect dropped client connections, though some details vary depending on the transport used. It is important to note that this mechanism is only used when something disrupts communication and does not properly terminate the TCP connection. It isn't used when a connection is deliberately closed or disconnected, for example by calling the [`close()` method](https://ably.com/docs/api/realtime-sdk/connection#close) or being disconnected by the server. The 15 second interval between heartbeats is used to strike a balance between optimizing battery usage for client devices and the time it takes to identify a dropped or unstable connection. The interval between heartbeats can be customized if your app requires increased battery preservation or to identify dropped connections more quickly. Set a value between 5000 and 1800000 milliseconds (5 seconds and 30 minutes) using the `heartbeatInterval` parameter within the `transportParams` property of the [`clientOptions`](https://ably.com/docs/api/realtime-sdk#client-options) object. Using a higher `heartbeatInterval` can increase the time taken for the Ably service and the client itself to identify a connection has dropped when an abrupt disconnect occurs. The number of [concurrent connections](https://ably.com/docs/platform/pricing/limits#connection) may also appear higher as it can take longer to terminate dropped connections. Although `heartbeatInterval` can be set as high as 30 minutes, Ably does not recommend setting it this high. You can also call [`ping()`](https://ably.com/docs/api/realtime-sdk/connection#ping) to send a heartbeat ping to Ably, which can be useful for measuring the true round-trip latency to the Ably server. The following example code demonstrates establishing a connection to Ably with a `heartbeatInterval` of 10 seconds: ```realtime_javascript const ably = new Ably.Realtime( { key: 'your-api-key', transportParams: { heartbeatInterval: 10000 } } ); ``` ```realtime_nodejs const ably = new Ably.Realtime( { key: 'your-api-key', transportParams: { heartbeatInterval: 10000 } } ); ``` ```realtime_go ably, err := ably.NewRealtime( ably.WithKey("your-api-key"), ably.WithTransportParams(url.Values{ "heartbeatInterval": {"10000"}, }), ) ``` ```realtime_java ClientOptions options = new ClientOptions("your-api-key"); options.transportParams = new Param[]{ new Param("heartbeatInterval", "10000") }; AblyRealtime ably = new AblyRealtime(options); ``` ## Browser page unload behavior In browser environments, ably-js automatically handles page unload events to ensure connections are properly closed when users navigate away or close pages. ### Default `beforeunload` behavior By default, the Ably Pub/Sub JavaScript SDK adds a listener for the `beforeunload` event to cleanly close connections before a page is closed. This provides orderly behavior where: * Connections are seen as having closed immediately by Ably servers. * Presence members associated with the connection are seen by all users as having left immediately. If a connection to Ably is not explicitly closed when there is a page unload event, then the connection state is preserved by Ably for 2 minutes. Preserving connection state for 2 minutes when there is an unexpectedly dropped connection provides the opportunity for the client to reconnect and resume the connection without losing any messages. ### Reliability considerations The `beforeunload` event can be unreliable and is not guaranteed to fire under certain circumstances: * The event may fire but the page is subsequently not disposed of (navigation can be cancelled). * The handler in ably-js that closes a connection on a `beforeunload` event is hazardous unless the application developer is certain that there is no case where `beforeunload` fires, but the page is subsequently not unloaded. * Recent releases of Chrome (version 108+) have introduced a Memory Saver feature that can cause pages to be discarded without firing `beforeunload` events. ### Chrome Memory Saver impact Chrome's Memory Saver feature assists with controlling the browser's memory footprint by discarding inactive tabs. This significantly increases the frequency of pages being discarded, which: * Causes JavaScript execution to stop without opportunity to intercept the event. * Prevents connections from closing immediately since `beforeunload` doesn't fire. * Results in longer delays before Ably recognizes the connection has closed. * Affects presence members leaving immediately. ### Managing connection lifecycle To ensure predictable connection closure behavior, consider these options: Set `closeOnUnload:false` in [`ClientOptions`](https://ably.com/docs/api/realtime-sdk#client-options) when initializing the library: ```realtime_javascript const ably = new Ably.Realtime({ key: 'your-api-key', closeOnUnload: false }); ``` ```realtime_nodejs const ably = new Ably.Realtime({ key: 'your-api-key', closeOnUnload: false }); ``` Manage connection lifecycle explicitly by calling [`close()`](https://ably.com/docs/api/realtime-sdk/connection#close) on the Ably realtime instance when it's no longer needed: ```realtime_javascript // When your application determines the connection should close ably.close(); ``` ```realtime_nodejs // When your application determines the connection should close ably.close(); ``` Disable Chrome Memory Saver globally or on a site-by-site basis in browser settings if the feature is impacting your application's behavior. ## Close a connection A connection to Ably should be closed once it is no longer needed. Note that there is a 2 minute delay before a connection is closed, if the [`close()`](https://ably.com/docs/api/realtime-sdk/connection#close) method hasn't been explicitly called. This is important to consider in relation to the number of [concurrent connections](https://ably.com/docs/platform/pricing/limits#connection) to your account. The following code sample explicitly closes the connection to Ably by calling the `close()` method and prints the message `Closed the connection to Ably`: ```realtime_javascript ably.close(); // runs synchronously console.log('Closed the connection to Ably.'); ``` ```realtime_nodejs ably.close(); // runs synchronously console.log('Closed the connection to Ably.'); ``` ```realtime_java ably.connection.close(); ably.connection.on(ConnectionEvent.closed, new ConnectionStateListener() { @Override public void onConnectionStateChanged(ConnectionStateChange state) { System.out.println("New state is " + state.current.name()); switch (state.current) { case closed: { // Connection closed System.out.println("Closed the connection to Ably."); break; } case failed: { // Failed to close connection break; } } } }); ``` ```realtime_python await ably.close() print('Closed the connection to Ably.') ``` ```realtime_ruby ably.connection.close ably.connection.on(:closed) do puts "Closed the connection to Ably!" end ``` ```realtime_swift ably.connection.close() ably.connection.on { stateChange in let stateChange = stateChange switch stateChange.current { case .closed: print("Closed the connection to Ably.") case .failed: print("Failed to close connection to Ably.") default: break } } ``` ```realtime_csharp ably.Connection.Close(); ably.Connection.On(ConnectionEvent.Closed, args => { Console.Out.WriteLine("Closed the connection to Ably."); }); ``` ```realtime_objc [ably.connection close]; [ably.connection on:ARTRealtimeConnectionEventClosed callback:^(ARTConnectionStateChange *stateChange) { NSLog(@"Closed the connection to Ably."); }]; ``` ```realtime_flutter realtime.connection.close(); realtime.connection .on(ably.ConnectionEvent.closed) .listen((ably.ConnectionStateChange stateChange) async { print('New state is: ${stateChange.current}'); switch (stateChange.current) { case ably.ConnectionState.closed: // Connection closed print('Closed the connection to Ably.'); break; case ably.ConnectionState.failed: // Failed to close connection break; default: break; } }); ``` ```realtime_go client.Connection.On(ably.ConnectionEventClosed, func(change ably.ConnectionStateChange) { fmt.Println("Closed the connection to Ably.") }) client.Close() ```