Skip to content

Commit 3c4b434

Browse files
authored
Fail fast on reverse connect listener startup (#4009)
# Description Fail reverse-connect listener startup immediately instead of logging bind failures and surfacing a later connection timeout. - Validate and stage configured and programmatic listener endpoints before replacing active configuration. - Open all listeners atomically and report endpoint-specific failures with `BadTcpEndpointUrlInvalid`, the transport-specific status, or `BadNoCommunication`. - Roll back listeners opened by a failed attempt without reacquiring the manager lock, including forced listener disposal when normal close fails. - Preserve meaningful `ServiceResultException` status codes through both `StartService` overloads. - Dispose replaced configuration watchers and reject callbacks from stale watcher generations. - Keep the intended shared-listener behavior: one long-lived `ReverseConnectManager` can accept connections from multiple Servers on the same Client URL. - Expand `Docs/ReverseConnect.md` with listener lifetime, shared-manager usage, diagnostics, and specification references. ## Validation - `ReverseConnectManagerTests`: net10.0 and net48. - `ReverseConnectHostTests`: net10.0 and net48. - Ordered reverse-connect listener reuse integration tests: net10.0. ## Related Issues - Relates to #3985. ## Checklist - [ ] I have signed the [CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf) and read the [CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md) doc. - [x] I have added tests that prove my fix is effective or that my feature works and increased code coverage. - [x] I have added all necessary documentation. - [x] I have verified that my changes do not introduce (new) build or analyzer warnings. - [ ] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [ ] I have addressed **all** PR feedback received.
1 parent 55bdb5b commit 3c4b434

6 files changed

Lines changed: 636 additions & 94 deletions

File tree

docs/ReverseConnect.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,39 @@ If the Client accepts the connection, a secure connection requires that the Clie
3636

3737
The auto-reconnect behavior on the Server is essential to any real application, because Clients close the Socket when the SecureChannel is closed. According to the specification a Server needs to abort the auto-reconnect if it receives a *BadTcpMessageTypeInvalid* code, because that is the error it will receive from peers that have not been upgraded to support the *ReverseHello*. Because of this a Client can use the same error code to tell the Server to stop reconnecting, if a user has rejected the connection. However, in this implementation, only if the Server is configured for a single connection it applies an extended timeout before reconnecting to the Client to reduce the overall traffic. In other configurations the Server keeps sending the *ReverseHello* messages at the configured time interval.
3838

39+
## Sharing a listener across multiple Servers
40+
41+
A reverse-connect listener remains bound for the lifetime of its `ReverseConnectManager`. Seeing the listener port remain in the `LISTENING` state after a Session is established is expected. The listening socket accepts additional transport connections while each accepted socket is handed to the Session that claimed its `ReverseHello` message. Dispose the manager when the listener should be released.
42+
43+
Use one shared `ReverseConnectManager` for all Servers that connect to the same Client URL. Register or wait for each Server separately by using its Server `EndpointUrl` and, preferably, its `ServerUri`. The fluent dependency-injection integration registers the manager as a singleton.
44+
45+
``` csharp
46+
using var manager = new ReverseConnectManager(telemetry);
47+
manager.AddEndpoint(new Uri("opc.tcp://client-host:65300"));
48+
manager.StartService(new ReverseConnectClientConfiguration
49+
{
50+
HoldTime = 15000,
51+
WaitTimeout = 20000
52+
});
53+
54+
Task<ITransportWaitingConnection> serverA = manager.WaitForConnectionAsync(
55+
new Uri("opc.tcp://server-a:4840"),
56+
"urn:example:server-a",
57+
cancellationToken);
58+
Task<ITransportWaitingConnection> serverB = manager.WaitForConnectionAsync(
59+
new Uri("opc.tcp://server-b:4840"),
60+
"urn:example:server-b",
61+
cancellationToken);
62+
63+
await Task.WhenAll(serverA, serverB).ConfigureAwait(false);
64+
```
65+
66+
Pass each returned `ITransportWaitingConnection` to the session factory for the corresponding Server.
67+
68+
Do not create a separate manager for each Server when those managers use the same local listener URL. Only one listener can bind a given host and port. `StartService` validates and opens all configured listener endpoints atomically. An invalid URL reports `BadTcpEndpointUrlInvalid`, an unsupported transport retains its transport-specific status, and a bind or listener-open failure reports `BadNoCommunication`. Startup diagnostics identify the affected endpoint URLs, and listeners opened by a failed attempt are closed instead of allowing a later connection wait to time out.
69+
70+
This behavior follows [OPC UA Part 6, 7.1.3](https://reference.opcfoundation.org/specs/OPC-10000-6/v1.05.07/7.1.3), which defines a separate transport connection for each reverse connection and requires Servers to maintain an available socket to each configured Client. [OPC UA Part 12, 4.4.2](https://reference.opcfoundation.org/specs/OPC-10000-12/v1.05.07/4.4.2) defines one or more Client URLs that allow Servers to connect. Clients shall validate the `ServerUri` and `EndpointUrl` as described in [OPC UA Part 2, 6.14](https://reference.opcfoundation.org/specs/OPC-10000-2/v1.05.06/6.14).
71+
3972
## Configuration Extensions
4073

4174
This configuration sample shows the configuration setting for a reverse connection on port 65300.

0 commit comments

Comments
 (0)