grpc-dotnet icon indicating copy to clipboard operation
grpc-dotnet copied to clipboard

Client-side RPC

Open MaxXor opened this issue 4 years ago • 5 comments

Hello, I'm planning to use gRPC for my next project, but as far as I've understood only RPC on the server are supported. The situation is that I want to connect multiple clients to a gRPC server and then let the server call methods on the connected clients. So basically, the reverse of current gRPC functionality. What would be the easiest way to achieve this behavior? Also, is there a way for the server to list all connected clients and address each one individually?

MaxXor avatar May 19 '20 09:05 MaxXor

For your first question I am not sure if what you're describing is possible at least the way you've described it. gRPC supports duplex streaming so you can communicate between client and server in both directions. That way you can selectively send messages to different clients and then how each client processes these messages is up to you.

Regarding your second question - When you call a RPC on the server, server-side you can add the caller to a concurrent dictionary that keeps some identifier for it as key and its IServerStreamWriter response stream as value. Then you would have a collection of all subscribers and can selectively write messages to them. Of course, you would also have to remove the caller from the dictionary once the RPC has been completed.

Here is an article with some code examples that might help you: https://damienbod.com/2019/03/25/grpc-bi-directional-streaming-with-razor-pages-and-a-hosted-service-grpc-client/

VasilSirakov avatar May 19 '20 12:05 VasilSirakov

There are many problems making a server call a client like firewalls and NAT addressing.

A better solution is for clients to connect to the server and establish an ongoing connection. The server can then send messages to the client on that connection.

JamesNK avatar May 19 '20 23:05 JamesNK

@VasilSirakov Thanks for your reply, by using duplex connections and streaming I would then have to build my own protocol on top of gRPC which is actually what I would like to avoid.

@JamesNK I think you've misunderstood me. Sorry I'll try to explain it better. The client still establishes the connection to the server. So firewalls and port forwarding stuff is not a problem. I just want to call methods on the clients from the server (given an established connection) in a way that's as elegant as doing the calls now on the server. Is there no other way than using the streaming?

MaxXor avatar May 20 '20 07:05 MaxXor

You may find issue https://github.com/grpc/grpc/issues/14101 about "tunneling" interesting. (This is the repository for the C code that drives most of the gRPC implementations out there today. Notably, grpc-dotnet doesn't use this code, but it needs to interoperate with it.)

There are unresolved design issues in this space, but that issue also talks about some approaches that others have used to approximate something like what you're looking for, including

  • an app-specific streaming protocol,
  • using a third-party proxy tool,
  • hooks available in some implementations for binding a gRPC channel to an already open TCP/HTTP connection, and
  • techniques like running gRPC over SSH port forwarding.

chwarr avatar May 20 '20 18:05 chwarr

Is there no other way than using the streaming?

Streaming is the only way I know how.

  1. The client establishes a server streaming call
  2. The client listens for incoming messages from the call and acts on them
  3. When the server wants to "invoke" the client it sends a message

In gRPC there is no state on the server of all the clients that are listening. If you want to send a message to all clients (i.e. broadcast the message) then you'll need to implement that yourself.

SignalR on the other hand is stateful, and records all the clients connected to a hub. That makes broadcasting a message to all clients easier.

JamesNK avatar May 20 '20 22:05 JamesNK