Vixen
dd8b0a81
csharp
public sealed class CompositeTransport

Several transports, listening at once, behind one.

No guide page documents this yet — the page shows what the code says about itself.

Remarks

What it is for is one server accepting more than one kind of client. A desktop build should be on UDP and a browser build cannot be; a game that wants both has otherwise to run two servers with two worlds, or pick one and make somebody suffer for it. Here the session, replication and RPC layers see a single transport and never learn that half their players arrived over TCP.

Connection ids are rewritten, and that is the whole of the difficulty. Each inner transport numbers its own connections from one, so two of them will hand out the same number for different players within the first second. This one hands out ids of its own and keeps a map both ways, so the number a game sees is unique across every transport it is listening on — which is what everything above assumes and what nothing above checks.

The client half is a single choice unless it is asked to be a race. Composing servers is the useful direction, and a client that knows what it is and which address it was given wants exactly the one it was told to — which is what the ordinary constructor gives it. Racing is the other case, and it is one server rather than two: a composite listening on UDP and on WebSocket 443 is reachable two ways, and a client racing them is a client that gets through a corporate firewall that drops UDP.

⚠ A race is resolved on the first transport-level connect unless it is asked to wait for the first inbound byte, and those are different questions. A handshake is NetworkSession's — its ConnectRequest and ConnectAccepted — and an ITransport sees connections, disconnections and opaque bytes. The transport-observable proxy for a completed handshake is first inbound data, and confirmWithin on Racing is what asks for it: a candidate that connects becomes provisional, the other routes keep running, and a provisional route that never says anything back loses on a budget counted in Poll's own elapsed. That is the middlebox which completes the connection and drops the payload — #1227.

⚠ And the second OnConnected that costs is one nothing above minds. The fallback needs the layer above to send its handshake down the new route, and the way to ask for that is to report the promotion as a connect — which NetworkSession's client arm handles by design: it abandons the earlier handshake span and sends a fresh ConnectRequest, and its own comment names "a transport that retries a route" as the case it keeps that rule for. So the composite needs neither a probe of its own nor a session-level notion of an attempt; it needs the session to be told to connect again, which is a thing it already does.

Nothing above ever learns that a race happened. A loser's connect, data and disconnect are swallowed with the candidate stopped — including the disconnect of a provisional route, which is the whole of the fallback: the layers above are told a connect, then possibly told a connect again on a different route, and are never told that either attempt died. One OnDisconnected reaches them, and only when there is no route left to try.

Fields and properties (6)

Methods (12)

  • public TransportLoss? LossFor(ConnectionId connection)
  • public CompositeTransport(IReadOnlyList<ITransport> transports, int clientTransport = 0)

    Combines transports.

  • public static CompositeTransport Racing(IReadOnlyList<ITransport> transports, TimeSpan stagger = default(TimeSpan), TimeSpan confirmWithin = default(TimeSpan))

    Combines transports and races the client half across every one of them: whichever connects first is the one the layers above ever hear about.

  • public void StartServer()

    Starts listening. Where — a port, an address, a relay — was fixed when the transport was constructed, which is what keeps this interface free of anything socket-shaped.

  • public void StopServer()

    Stops listening and disconnects everyone, reporting ServerStopped to both sides. Does nothing if the server half is already stopped.

  • public void StartClient()
  • public void StopClient()

    Disconnects the client half, reporting Requested to this side and RemoteRequested to the server. Does nothing if the client half is already stopped.

  • public void Disconnect(ConnectionId connection)

    Closes one connection from the server side, reporting Requested here and Kicked there. Does nothing if the connection is not one of ours.

  • public void SendToClient(ConnectionId connection, ReadOnlySpan<byte> payload, Channel channel)

    Sends to one connected client. Does nothing if the connection is not ours.

  • public void SendToServer(ReadOnlySpan<byte> payload, Channel channel)
  • public void Poll(TimeSpan elapsed, ITransportEvents events)

    Advances the transport by and reports everything that has happened since the last call, in the order it happened, to .

  • public void Dispose()

    Disposes every transport underneath.

Used by (4)

  • CompositeClientRaceTestsVixen.Net.Transport.Composite.Tests
  • CompositeOverLocalConformanceTestsVixen.Net.Transport.Composite.Tests
  • CompositeTransportTestsVixen.Net.Transport.Composite.Tests
  • RouterVixen.Net.Transport.Composite