The connection manager

Introduction

The connection manager is the first piece of the group chat features. This class manages connections to peers and offer to the user multiplexed sockets to devices they want to connect. For example, if Alice wants to be connected to one of Bob’s device to transfer 2 files, she will ask the ConnectionManager to open 2 channels (one per file) to Bob. This will give:

    aliceAccount->connectionManager().connectDevice(bobDeviceId, "file://file1",
        [](std::shared_ptr<ChannelSocket> socket) {
        if (socket) {
            // transfer first file
        }
    });

    aliceAccount->connectionManager().connectDevice(bobDeviceId, "file://file2",
        [](std::shared_ptr<ChannelSocket> socket) {
        if (socket) {
            // transfer second file
        }
    });

Behind that, the ConnectionManager will first connect to Bob’s device via the DHT (via ICE) and setup a TLS Socket. Then, it will ask for a channel, and when the channel is ready, inform Alice via a callback. For the second file, it will use the first socket and will just open a new channel (only needs 2 TLS packet, so it’s fast)

DHT side

It’s the same as calls, see Exchange ICE candidates, ICE negotiation, Encrypt the control socket but only in TCP.

However, when a side receives a new ICE request, the callback set by void onICERequest(onICERequestCallback&& cb); is triggered.

Negotiating a new channel

A channel is defined by an id (unique) and a uri (not unique). For example (1, ‘git://*’)

When ready, the ConnectionManager considers that the channel 0 exists. This channel is called the CONTROL channel and is used to ask for new channels.

The protocol used is pretty simple and looks like the RTP protocol:

  1. 16 bits are used to store the length of the body.

  2. 16 bits for the channel id (destination)

  3. body

So all packets have a 32 bits len header.

To ask for a new channel, the ConnectionManager will send a ChannelRequest object (msgpack is used to serialize the struct) in the channel 0 to send the id and the name of the new channel to the peer (with isAnswer = false). The peer will call the callback given with ̀ void onChannelRequest(ChannelRequestCallBack&& cb); and will refuse or accept the request. If accepted, the peer will answer with a ChannelRequest with the same data (and ̀isAnswer = true`) and then both peers callbacks will be triggered to inform that the ChannelSock is usable.

Closing a channel

A EOF is transmitted for a channel if the length of the content is 0.

Structure of the connectionManager

Ownership

  1. A JamiAccount owns the ConnectionManager and have access to the ChannelSocket objects (shared_ptr owned with the MultiplexedSocket.

  2. The ConnectionManager owns MultiplexedSockets and ICE objects

  3. MultiplexedSockets owns the TLS transport and the ChannelSocket objects

  4. ChannelSocket owns the data buffers

Roles

  1. ConnectionManager is used to manage connections to peers.

  2. MultiplexedSockets are used to send data over the TLSSocket, read the incoming packets and manage channels.

  3. ChannelSockets are used by the client to interact with the other peer.

Usage

Scenarios are described in the corresponding unit tests (test/unitTest/connectionManager/connectionManager.cpp)