Connection Establishment

7 min read

7 min read

7 min read

7 min read

7 min read

7 min read

7 min read

7 min read

7 min read

Ice establishes connections on demand. When an invocation on a proxy needs a connection, Ice reuses an existing connection if it can, and otherwise establishes a new connection to one of the proxy's endpoints.

Ice reuses connections at two levels:

  • By default, a proxy caches the connection it obtains and uses it for its subsequent invocations.
  • The communicator keeps the connections it establishes, and other proxies of this communicator share them.

When a proxy with connection caching enabled needs a connection, Ice prefers an endpoint that already has a connection. It reuses an existing connection that meets the following conditions, and establishes a new connection only when no existing connection does:

  • The connection's remote endpoint matches one of the proxy's endpoints.
  • The connection was established for the proxy's connection ID.

You disable connection caching with the ice_connectionCached proxy methodproxy methodproxy methodproxy methodproxy methodproxy methodproxy methodproxy method or the name.ConnectionCached property. Ice then selects an endpoint for each invocation on this proxy, in the order set by the endpoint selection policy and whether or not the endpoint already has a connection, and reuses an existing connection to this endpoint if there is one. With the default Random policy, disabling connection caching spreads a proxy's invocations across its endpoints.

The communicator manages the lifetime of its connections independently of the proxies that use them. Connections are long-lived: a healthy connection with regular activity stays open until the communicator is destroyed. The communicator closes a connection after a period of inactivity, 300 seconds by default, aborts a connection when a read or write fails or when the idle check fails, and closes all its connections when it is destroyed. When the connection a proxy caches closes, the proxy obtains a connection again for its next invocation.

When a proxy needs a connection, Ice starts from the proxy's endpoints, or from the endpoints its locator returns for an indirect proxy, and produces the list of endpoints it can connect to as follows:

  1. It removes the endpoints of unknown transports.
  2. It removes the endpoints that do not suit the proxy's invocation mode: datagram endpoints for twoway, oneway and batch oneway proxies, and non-datagram endpoints for datagram and batch datagram proxies. If no endpoint remains, the invocation fails with NoEndpointException. For example, a twoway proxy whose only endpoint is a UDP endpoint has no endpoint left.
  3. It orders the endpoints according to the endpoint selection policy. Random, the default, shuffles the endpoints; Ordered keeps their order.
  4. It resolves host names into IP addresses. When a host name resolves to several addresses, the list holds them in the order the DNS resolver returns them, with IPv4 addresses ahead of IPv6 addresses unless Ice.PreferIPv6Address is set. On iOS, the list holds the host names of TCP and SSL endpoints, not their addresses: the system resolves them when Ice connects.The list holds the host names, not their addresses: the JavaScript runtime resolves them when Ice connects.

You set the endpoint selection policy with the Ice.Default.EndpointSelection property, the name.EndpointSelection proxy property, or the ice_endpointSelection proxy methodproxy methodproxy methodproxy methodproxy methodproxy methodproxy methodproxy method.

With connection caching enabled, Ice first reuses an existing connection to any endpoint in the list, so the connection it reuses is not necessarily to the first endpoint, even with the Ordered policy. When no existing connection qualifies, or when connection caching is disabled, Ice attempts to connect to each endpoint, and to each address of an endpoint, in order, until an attempt succeeds.

Each proxy has a connection ID, an empty string by default, and Ice reuses a connection for a proxy only if the connection was established for the same connection ID. The ice_connectionId proxy methodproxy methodproxy methodproxy methodproxy methodproxy methodproxy methodproxy method returns a new proxy with the given connection ID. Proxies with the same connection ID form a group that shares connections, and Ice establishes a new connection for a group that has none to a suitable endpoint.

The following example establishes three connections:

C++
SomePrx prx{communicator, "ident:tcp -p 10000"};
auto g1 = prx.ice_connectionId("group1");
auto g2 = prx.ice_connectionId("group2");
prx.ice_ping(); // Opens a new connection
g1.ice_ping(); // Opens a new connection
g2.ice_ping(); // Opens a new connection
auto i1 = g1.ice_facet<AdminPrx>("admin");
i1.ice_ping(); // Reuses g1's connection
auto i2 = prx.ice_connectionId("group2").ice_facet<AdminPrx>("admin");
i2.ice_ping(); // Reuses g2's connection
C#
var prx = SomePrxHelper.createProxy(communicator, "ident:tcp -p 10000");
var g1 = prx.ice_connectionId("group1");
var g2 = prx.ice_connectionId("group2");
await prx.ice_pingAsync(); // Opens a new connection
await g1.ice_pingAsync(); // Opens a new connection
await g2.ice_pingAsync(); // Opens a new connection
var i1 = AdminPrxHelper.uncheckedCast(g1.ice_facet("admin"));
await i1.ice_pingAsync(); // Reuses g1's connection
var i2 = AdminPrxHelper.uncheckedCast(
prx.ice_connectionId("group2").ice_facet("admin"));
await i2.ice_pingAsync(); // Reuses g2's connection
Java
var prx = SomePrx.createProxy(communicator, "ident:tcp -p 10000");
var g1 = prx.ice_connectionId("group1");
var g2 = prx.ice_connectionId("group2");
prx.ice_ping(); // Opens a new connection
g1.ice_ping(); // Opens a new connection
g2.ice_ping(); // Opens a new connection
var i1 = AdminPrx.uncheckedCast(g1.ice_facet("admin"));
i1.ice_ping(); // Reuses g1's connection
var i2 = AdminPrx.uncheckedCast(prx.ice_connectionId("group2").ice_facet("admin"));
i2.ice_ping(); // Reuses g2's connection
TypeScript
const prx = new SomePrx(communicator, "ident:tcp -p 10000");
const g1 = prx.ice_connectionId("group1");
const g2 = prx.ice_connectionId("group2");
await prx.ice_ping(); // Opens a new connection
await g1.ice_ping(); // Opens a new connection
await g2.ice_ping(); // Opens a new connection
const i1 = AdminPrx.uncheckedCast(g1.ice_facet("admin"));
await i1.ice_ping(); // Reuses g1's connection
const i2 = AdminPrx.uncheckedCast(
prx.ice_connectionId("group2").ice_facet("admin"));
await i2.ice_ping(); // Reuses g2's connection
MATLAB
prx = SomePrx(communicator, 'ident:tcp -p 10000');
g1 = prx.ice_connectionId('group1');
g2 = prx.ice_connectionId('group2');
prx.ice_ping(); % Opens a new connection
g1.ice_ping(); % Opens a new connection
g2.ice_ping(); % Opens a new connection
i1 = AdminPrx.uncheckedCast(g1.ice_facet('admin'));
i1.ice_ping(); % Reuses g1's connection
i2 = AdminPrx.uncheckedCast(prx.ice_connectionId("group2").ice_facet("admin"));
i2.ice_ping(); % Reuses g2's connection
PHP
$prx = SomePrxHelper::createProxy($communicator, "ident:tcp -p 10000");
$g1 = $prx->ice_connectionId("group1");
$g2 = $prx->ice_connectionId("group2");
$prx->ice_ping(); // Opens a new connection
$g1->ice_ping(); // Opens a new connection
$g2->ice_ping(); // Opens a new connection
$i1 = AdminPrxHelper::uncheckedCast($g1->ice_facet("admin"));
$i1->ice_ping(); // Reuses g1's connection
$i2 = AdminPrxHelper::uncheckedCast(
$prx->ice_connectionId("group2")->ice_facet("admin"));
$i2->ice_ping(); // Reuses g2's connection
Python
prx = SomePrx(communicator, "ident:tcp -p 10000")
g1 = prx.ice_connectionId("group1")
g2 = prx.ice_connectionId("group2")
await prx.ice_pingAsync() # Opens a new connection
await g1.ice_pingAsync() # Opens a new connection
await g2.ice_pingAsync() # Opens a new connection
i1 = AdminPrx.uncheckedCast(g1.ice_facet("admin"))
await i1.ice_pingAsync() # Reuses g1's connection
i2 = AdminPrx.uncheckedCast(
prx.ice_connectionId("group2").ice_facet("admin"))
await i2.ice_pingAsync() # Reuses g2's connection
Ruby
prx = SomePrx.new(communicator, "ident:tcp -p 10000")
g1 = prx.ice_connectionId("group1")
g2 = prx.ice_connectionId("group2")
prx.ice_ping() # Opens a new connection
g1.ice_ping() # Opens a new connection
g2.ice_ping() # Opens a new connection
i1 = AdminPrx::uncheckedCast(g1.ice_facet("admin"))
i1.ice_ping() # Reuses g1's connection
i2 = AdminPrx::uncheckedCast(
prx.ice_connectionId("group2").ice_facet("admin"))
i2.ice_ping() # Reuses g2's connection
Swift
let prx = try makeProxy(
communicator: communicator,
proxyString: "ident:tcp -p 10000",
type: SomePrx.self)
let g1 = prx.ice_connectionId("group1")
let g2 = prx.ice_connectionId("group2")
try await prx.ice_ping() // Opens a new connection
try await g1.ice_ping() // Opens a new connection
try await g2.ice_ping() // Opens a new connection
let i1 = uncheckedCast(prx: g1.ice_facet("admin"), type: AdminPrx.self)
try await i1.ice_ping() // Reuses g1's connection
let i2 = uncheckedCast(
prx: prx.ice_connectionId("group2").ice_facet("admin"), type: AdminPrx.self)
try await i2.ice_ping() // Reuses g2's connection

prx, g1 and g2 each establish a connection because their connection IDs differ. i1 inherits its connection ID from g1 and shares the group1 connection; i2 is configured with group2 and shares the connection of g2.

When a connection attempt fails, Ice tries the next address or endpoint, until it establishes a connection or has tried them all.

When every attempt fails, the invocation fails. An automatic retry of the invocation goes through connection establishment again.

A connection attempt fails with ConnectTimeoutException when it does not complete within the time set by Ice.Connection.Client.ConnectTimeout, 10 seconds by default.

Ice binds an outgoing connection to the source address of the endpoint it connects to. You set this address with the --sourceAddress endpoint option, and Ice gives the address set by Ice.Default.SourceAddress to the endpoints it parses without this option, such as those of a stringified proxy. An endpoint that Ice unmarshals, such as an endpoint returned by a locator, has no source address, and the operating system then selects the source address. Both settings take a numeric IP address. On iOS, Ice does not bind TCP and SSL connections to a source address.

In Ice for JavaScript, this applies to TCP connections in Node.js. WebSocket connections use the source address selected by the operating system.