Archived

This forum has been archived. Please start a new discussion on GitHub.

Javascript and ACM - keep the connection alive

sulliwane
edited August 2016 in Help Center

Hello,

My Javascript ice client connects to a ICE server (no glacier). I register some callbacks using this (in my .ice):

int setCallBack(Ice::Identity ident);

so that the server can send me data when he wants (bi-direction). This means that I need to keep the connection alive.

My question:
What is the best solution to keep the two-way connection between my client and the server? And how to throw an error on the client side when the connection is obviously down?

Form my understanding, there is two classic answers to this -very common- problem:

Solution 1:
The client calls a remote function every x seconds to keep the connection active (ex: a Heartbeat function that return 0).
I set the timeout id.properties.setProperty('Ice.Default.InvocationTimeout', '3000'); and when I disconnect network, I catch the error and try to reconnect.

Solution 2:
Use ACM heartbeat to keep the connection alive, and timeouts to judge when a connection is invalid.

Here is my client code for this second solution. Is this the proper way to keep the connection alive (using ACM)? :

const id = new Ice.InitializationData();
id.properties = Ice.createProperties();

communicator = Ice.initialize(process.argv, id);

const proxy = communicator.stringToProxy(iceUrl);
server = await Trade.TdSessionPrx.checkedCast(proxy);
const adapter = await communicator.createObjectAdapter('');
const r = adapter.addWithUUID(new CallbackReceiverI());
proxy.ice_getCachedConnection().setAdapter(adapter);
proxy.ice_getCachedConnection().setACM(3000, undefined, Ice.ACMHeartbeat.HeartbeatAlways);
proxy.ice_getCachedConnection().setCallback({
closed() {
   debug('closed');
},
heartbeat() {
  debug('heartbeat');
},
});
setCallbackReturn = await server.setCallBack(r.ice_getIdentity());

What I am supposed to do in closed() and heartbeat() function? And are these functions called locally or are they called by the server? How to catch a connection problem? Should I reconnect on a catch, or ACM will try reconnect for me?

Many thanks for your help :)

Tagged:

Comments

  • sulliwane
    edited August 2016

    I actually found many answers on this documentation page: https://doc.zeroc.com/pages/viewpage.action?pageId=14031635#Ice.ACM.*-Ice.ACM.Close

    One remaining question:

    Given these ACM settings:

    Client:
    Ice.ACM.Close: 0
    Ice.ACM.Heartbeat: 0
    Ice.ACM.Timeout: 2
    
    Server:
    Ice.ACM.Close: 4
    Ice.ACM.Heartbeat: 0
    Ice.ACM.Timeout: 5
    

    For an idling connection, The server invoke my client close() callback after 10s (this value should be explained by the related server setting Ice.ACM.Timeout: 5 I guess).

    -> Why isn't it my client who locally close the connection after 2 seconds (as specified in its settings Ice.ACM.Timeout: 2) ?

    The decision to close the idling connection can be taken on the client side right? If the connection is closed by client side ACM, will it invoke the close() callback as well?

    Thanks for your answers!

    EDIT: I replace client settings like this Ice.ACM.Close: 4 and everything works as expected.

  • sulliwane
    edited August 2016

    One question subsists:

    What is the correct procedure to reconnect after a network failure (using ACM)?
    given these client parameters:

    Client:
    Ice.ACM.Close: 4
    Ice.ACM.Heartbeat: 0
    Ice.ACM.Timeout: 5
    

    Is it enough to handle the reconnection logic in the close() callback (that should be called once the network connection is back, if I'm correct) ?

    Many thanks

  • benoit
    benoit Rennes, France

    Hi,

    ACM is a local facility to monitor Ice connections opened with a given Ice communicator. The close and heartbeat callbacks are invoked locally by the Ice connection object when the connection receives a heartbeat message from the peer or when the connection detects it has been closed (either gracefully or forcefully for various reasons).

    These callbacks are just notifications which your application can use to report the status of the connection or take action when it's notified that the connection is closed. If you have a UI for example which relies on the Ice connection to remain open for the duration of a session and if the connection gets closed, you can show a UI alert message and tear down the application to require the user to sign-in again.

    If you want to keep the connection alive, you will need to enable heartbeats by setting Ice.ACM.Heartbeat to a non zero value.

    Typically, when a application which rely on a connection remaining open detects the connection has been closed, it tears down all the state associated with the connection and re-connect. This usually involves authenticating again with the server and registering the callbacks with the server using the new connection.

    I recommend to look at our Chat demo for additional information on these concepts, we describe the chat demo here: https://doc.zeroc.com/display/Doc/Chat+Demo

    You'll also find the code for the JavaScript chat client here: https://github.com/zeroc-ice/ice-demos/tree/3.6/js/Chat

    Cheers,
    Benoit.

  • sulliwane
    edited September 2016

    Hi Benoit, Many thanks for your answer.

    I understand better now. Still, a little problem remains:

    When my client directly connect to an ICE, after I disconnect the client networking for say 10 seconds (more than the timeout) and then reconnect the network, when network access is reestablished the closed() callback is invoked.

    So the closed() callback is not invoked on disconnection, but on reconnection. I'm not sure if it's normal, but it's convenient I find, because if no network there is no point in trying to reconnect.

    Happy with that, I then tried connecting with the same settings to an ICE server through GLACIER2. Both ICE and GLACIER2 have set ACM, as well as my local client. But this time, no closed() callback is invoked on reconnection (neither on network disconnection).

    Any idea where is the problem?

    Cheers!

    for information, here are the client / server ACM settings:

    Client (javascript):
    Ice.ACM.Close: 4
    Ice.ACM.Heartbeat: 3
    Ice.ACM.Timeout: 5
    
    server (ICE/GLACIER2):
    Ice.ACM.Close: 4
    Ice.ACM.Heartbeat: 0
    Ice.ACM.Timeout: 5
    

    and here is my javascript client code:

    const id = new Ice.InitializationData();
        id.properties = Ice.createProperties();
        id.properties.setProperty('Ice.Default.InvocationTimeout', '30000');
        id.properties.setProperty('Ice.ACM.Close', '4');
        id.properties.setProperty('Ice.ACM.Heartbeat', '3');
        id.properties.setProperty('Ice.ACM.Timeout', '5');
        id.properties.setProperty('Ice.Default.Router', glacierRouterUrl);
        communicator = Ice.initialize(process.argv, id);
        const OnMdServerCallback = new Ice.Class(MdLive.MdSessionCallBack, iceLiveReadableCallback);
        router = communicator.getDefaultRouter();
    
        router = await Glacier2.RouterPrx.checkedCast(router);
    
        session = await router.createSession('user', 'password');
        session = await MdLive.MdSessionPrx.uncheckedCast(session);
    
        const [timeout, category, adapter] = await Promise.all([
          router.getSessionTimeout(),
          router.getCategoryForClient(),
          communicator.createObjectAdapterWithRouter('', router),
        ]);
    
        const callback = MdLive.MdSessionCallBackPrx.uncheckedCast(
          adapter.add(new OnMdServerCallback(), new Ice.Identity('callback', category))
        );
    
        router.ice_getCachedConnection().setCallback({
          closed() {
            debug('closed(), take action to reconnect');
          },
          heartbeat() {
            debug('server sent heartbeat');
          },
        });
    
        setCallbackReturn = await session.setCallBack(callback);
    

    remark: And if I disable heartbeat on my client, and enable it on the server side, my client heartbeat() callback is never invoked by the server, and so the connection is closed after the timeout (true for just ICE and for ICE+GLACIER2).

  • benoit
    benoit Rennes, France

    Hi,

    Could you tell us a little more about your environment (operating system, Ice version, browser, etc)?

    The closed callback is invoked by Ice as soon as Ice detects the connection closure. How quickly it will detect the connection closure is really dependent on the environment and operating system.

    In your case, it appears that the JavaScript client is still able to send heartbeats even when the network is down so the disconnection isn't detected quickly. It's only when you bring the network back up that the system/browser notifies Ice that the previous connection is dead. When this occurs, Ice calls the closed callback.

    I don't think it's a good idea to rely on this behavior as it might be different depending on the system/browser.

    You could try configuring the sending of the heartbeats on Glacier2 instead of the client to get a quicker detection of the connection closure on the client side. The client will close the connection as soon as it no longer receives heartbeats for the timeout period.

    Instead of configuring Ice.ACM.* properties for Glacier2, you should configure Glacier2.Client.ACM.* properties. Glacier2 uses different ACM settings for the client or server endpoints. So you can try:

    Glacier2.Client.ACM.Close=4
    Glacier2.Client.ACM.Heartbeat=0
    Glacier2.Client.ACM.Timeout=5
    

    Let us know how it goes!

    Cheers,
    Benoit.

  • Thanks a lot Benoit, with these new settings Glacier2 ACM is effective! I will keep the hearbeat client side for now, and consider move it to the server side If I have problems. Need some time to test it now :)