icerpc / icerpc-csharp

A C# RPC framework built for QUIC, with bidirectional streaming, first-class async/await, and Protobuf support.
https://docs.icerpc.dev
Apache License 2.0
101 stars 13 forks source link

New TCP fallback example #3911

Closed bernardnormier closed 7 months ago

bernardnormier commented 7 months ago

This PR adds a new example that shows how to setup a client and server to communicate over QUIC and fallback to TCP if the connection cannot be established.

Once merged, I'll create the Protobuf equivalent of this example.

See also: https://github.com/orgs/icerpc/discussions/12

bernardnormier commented 7 months ago

Sample output:

Server

dbug: IceRpc.Server[11]
      Listener 'icerpc://[::0]?transport=quic' has started accepting connections
dbug: IceRpc.Server[11]
      Listener 'icerpc://[::0]?transport=tcp' has started accepting connections
dbug: IceRpc.Server[0]
      Listener 'icerpc://[::0]?transport=quic' accepted connection from '127.0.0.1:36147'
dbug: IceRpc.Server[3]
      Server connection from '127.0.0.1:4062' to '127.0.0.1:36147' connected
Dispatching greet request { name = 'vscode' }
dbug: IceRpc.Server[6]
      Server connection from '127.0.0.1:4062' to '127.0.0.1:36147' shutdown
dbug: IceRpc.Server[5]
      Server connection from '127.0.0.1:4062' to '127.0.0.1:36147' disposed
^Cdbug: IceRpc.Server[12]
      Listener 'icerpc://[::0]?transport=quic' has stopped accepting connections
dbug: IceRpc.Server[12]
      Listener 'icerpc://[::0]?transport=tcp' has stopped accepting connections

Client

vscode ➜ .../examples/slice/TcpFallback/Client (tcp-fallback) $ dotnet run
info: IceRpc.Logger.LoggerInterceptor[0]
      Sent request greet to icerpc:/VisitorCenter.Greeter over 127.0.0.1:36147<->127.0.0.1:4062 and received a response with status code Ok

Client with Secure server

vscode ➜ .../examples/slice/TcpFallback/Client (tcp-fallback) $ dotnet run
info: IceRpc.ClientConnection[0]
      Failed to connect to server using QUIC, falling back to TCP
      IceRpc.IceRpcException: An IceRpc call failed with error 'ServerUnreachable'.
       ---> System.Net.Sockets.SocketException (0x00000071): No route to host
         at System.Net.Quic.QuicConnection.HandleEventShutdownInitiatedByTransport(_SHUTDOWN_INITIATED_BY_TRANSPORT_e__Struct& data)
         at System.Net.Quic.QuicConnection.HandleConnectionEvent(QUIC_CONNECTION_EVENT& connectionEvent)
         at System.Net.Quic.QuicConnection.NativeCallback(QUIC_HANDLE* connection, Void* context, QUIC_CONNECTION_EVENT* connectionEvent)
      --- End of stack trace from previous location ---
         at System.Net.Quic.ValueTaskSource.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
         at System.Net.Quic.QuicConnection.FinishConnectAsync(QuicClientConnectionOptions options, CancellationToken cancellationToken)
         at System.Net.Quic.QuicConnection.<ConnectAsync>g__StartConnectAsync|2_0(QuicClientConnectionOptions options, CancellationToken cancellationToken)
         at System.Net.Quic.QuicConnection.<ConnectAsync>g__StartConnectAsync|2_0(QuicClientConnectionOptions options, CancellationToken cancellationToken)
         at IceRpc.Transports.Quic.Internal.QuicMultiplexedClientConnection.ConnectAsync(CancellationToken cancellationToken) in /workspaces/icerpc-csharp/src/IceRpc.Transports.Quic/Internal/QuicMultiplexedConnection.cs:line 140
         --- End of inner exception stack trace ---
         at IceRpc.Transports.Quic.Internal.QuicMultiplexedClientConnection.ConnectAsync(CancellationToken cancellationToken) in /workspaces/icerpc-csharp/src/IceRpc.Transports.Quic/Internal/QuicMultiplexedConnection.cs:line 150
         at IceRpc.Internal.IceRpcProtocolConnection.<>c__DisplayClass36_0.<<ConnectAsync>g__PerformConnectAsync|0>d.MoveNext() in /workspaces/icerpc-csharp/src/IceRpc/Internal/IceRpcProtocolConnection.cs:line 139
      --- End of stack trace from previous location ---
         at IceRpc.Internal.IceRpcProtocolConnection.<DisposeAsync>g__PerformDisposeAsync|37_0() in /workspaces/icerpc-csharp/src/IceRpc/Internal/IceRpcProtocolConnection.cs:line 280
info: IceRpc.Logger.LoggerInterceptor[0]
      Sent request greet to icerpc:/VisitorCenter.Greeter over [::ffff:127.0.0.1]:39964<->[::ffff:127.0.0.1]:4062 and received a response with status code Ok
Hello, vscode!
bernardnormier commented 7 months ago

I think this code would be simpler to do the connection creation all in line with the rest of the code.

I can't find a way to write this in an elegant manner. Give it a try!

For consistency it might also be interesting to connect the tcp connection before giving it to the pipeline.

I don't see the point - it would add lines of code for no clear purpose.

I think another option would be to use a model similar to our connection cache that hands out the connection during dispatch.

There are naturally several options on the client-side. I am trying here to do the simplest/clearest.