I want to create Client and Server through gRPC Server Streaming.
I would like to distinguish each client at this time. I looked into it, and I think we can choose using Uid to distinguish client or use peer to distinguish it.
What I am curious about is that when I want to send a response to only one specific client, I wonder if there is only a way to distinguish it through uid or peer after sending it to everyone. Can't the server send it to only one client from the beginning?
For example, suppose you created a server streaming grpc service called send(), request send() from multiple clients and wait for a response through stream. The server needs to respond to send() to a particular client, but I don't know how to generate only the send() service for this particular client.
One solution (but there might be better solutions) could be to pass a unique ID from the client to the server in the metadata, and have a centralized storage, like a singleton client manager class, for all connected clients on the server-side. This client manager class decides to who streams happen, and to who not.
For example, on the client-side the machine name is passed in the metadata when subscribing to the server stream as a identifier with the key client-id (though something like a GUID would probably more unique):
// client-side method that subscribes to the gRPC client
public async void Subscribe()
{
// subscribe to the server stream with a unique client id
_call = _serviceClient.SubscribeToServerStream(new Empty(), headers: new Metadata
{
new Metadata.Entry("client-id", Environment.MachineName)
});
// handle incoming messages from the server for this client
await HandleIncomingServerMessages(_call.ResponseStream);
}
The server-side code is kinda simplified, but I think it explains the idea. The client-id gets extracted from the metadata, and added to the (singleton) _clientManager class, which holds all connected clients and manages to who streams should happen. Now if you want to stream to this connected client, the centralized client manager can set (just as an example) this client's streaming property to active, and you can check in the outgoing stream if this client is active or not (with the id you extracted from the call context), and therefore stream messages or not.
// server-side method in the gRPC service that implements the stream
public override async Task SubscribeToServerStream(Empty request,
IServerStreamWriter<Message> responseStream,
ServerCallContext context)
{
// extract the unique client id
_clientId = context.RequestHeaders.FirstOrDefault(m => String.Equals(m.Key, "client-id", StringComparison.Ordinal));
// register it to the server-side client manager
_clientManager.AddNewConnectedClient(_clientId);
while(_isStreaming)
{
if(_clientManager.IsActiveClient(this._clientId))
{
// this client is set to active in the manager, therefore stream
}
else
{
// do nothing, since you currently do not want to stream to this connected client
}
}
}
I am pretty sure this will work, since I implemented something very similar not long ago, although it was kinda hacky (where multiple clients could connect, but only one of them could receive a stream at the same time, and there were some other conditions). But there might be better solutions to your problem than my answer, and I sadly do not have the time to write down and debug all the code to give you something that works out of the box.
Related
I'm using SingalR in an application that sends alot messages in a short period of time.
let's say i have client A and client B.
Client A just sends messages and client B just listening to messages.
Client A sends the following messages in the following order: A->B->C->D
What i'm seeing is that Client B sometimes receives the messages in a different order, for example: B->A->C->D
It is important for maintain the same order i sent the messages.
I've looked online and i found people saying i should use async-await on the function on the hub that handles those messages.
public async Task hubMethod(msgObject msg)
{
await Clients.All.message(msg);
}
I'm not sure how that helps since each time i make a call from client A , singalR should create a new instance of the hub.
The only thing it does is wait for the singalR that it finished doing all it can do on the server in order to send the message to the other client and notifies it to client A.
So my question is this - is there a singalR or asp.net mechanism that make sure i receive the messages in the correct order on the other client or do i need to write my own mechanism (server or client) that reorders the messages if they are out of order - and if so, is there a library that already does it?
You need to write your own mechanism. SignalR in client B has no way to know in which order the clients messages were sent by client A because there is many things that could delay a specific message arrival, like network delay, the only thing SignalR can guarantee is the order in which the messages arrived.
If you really need to know the original order of the messages you could put a count inside the message and let client B sort them out. However i suggest you try another approach, because guaranteeing the order of delivery is not a easy task.
I am building a c#/wpf project.
It's architecture is this:
A console application which will be on a virtual machine (or my home computer) that will be the server side.
A wpf application that will be the client app.
Now my problem is this - I want the server to be able to send changes to the clients. If for example I have a change for client ABC, I want the server to know how to call a service on the clients computer.
The problem is, that I don't know how the server will call the clients.
A small example in case I didn't explain it well:
The server is on computer 1, and there are two clients, on computers 2 and 3.
Client 2 has a Toyota car and client 3 has a BMW car.
The server on computer 1 wants to tell client 2 that it has a new car, an Avenger.
How do I keep track and call services on the clients?
I thought of saving their ip address (from calling ipconfig from the cmd) in the DB - but isn't that based on the WI-FI/network they are connected to?
Thanks for any help!
You could try implementing SignalR. It is a great library that uses web sockets to push data to clients.
Edit:
SignalR can help you solve your problem by allowing you to set up Hubs on your console app (server) that WPF application (clients) can connect to. When the clients start up you will register them with a specified Hub. When something changes on the server, you can push from the server Hub to the client. The client will receive the information from the server and allow you to handle it as you see fit.
Rough mockup of some code:
namepsace Server{}
public class YourHub : Hub {
public void SomeHubMethod(string userName) {
//clientMethodToCall is a method in the WPF application that
//will be called. Client needs to be registered to hub first.
Clients.User(userName).clientMethodToCall("This is a test.");
//One issue you may face is mapping client connections.
//There are a couple different ways/methodologies to do this.
//Just figure what will work best for you.
}
}
}
namespace Client{
public class HubService{
public IHubProxy CreateHubProxy(){
var hubConnection = new HubConnection("http://serverAddress:serverPort/");
IHubProxy yourHubProxy = hubConnection.CreateHubProxy("YourHub");
return yourHubProxy;
}
}
}
Then in your WPF window:
var hubService = new HubService();
var yourHubProxy = hubService.CreateHubProxy();
yourHubProxy.Start().Wait();
yourHubProxy.On("clientMethodToCall", () => DoSometingWithServerData());
You need to create some kind of subscription model for the clients to the server to handle a Publish-Subscribe channel (see http://www.enterpriseintegrationpatterns.com/patterns/messaging/PublishSubscribeChannel.html). The basic architecture is this:
Client sends a request to the messaging channel to register itself as a subscriber to a certain kind of message/event/etc.
Server sends messages to the channel to be delivered to subscribers to that message.
There are many ways to handle this. You could use some of the Azure services (like Event hub, or Topic) if you don't want to reinvent the wheel here. You could also have your server application track all of these things (updates to IP addresses, updates to subscription interest, making sure that messages don't get sent more than once; taking care of message durability [making sure messages get delivered even if the client is offline when the message gets created]).
In general, whatever solution you choose is plagued with a common problem - clients hide behind firewalls and have dynamic IP addresses. This makes it difficult (I've heard of technologies claiming to overcome this but haven't seen any in action) for a server to push to a client.
In reality, the client talks and the server listens and response. However, you can use this approach to simulate a push by;
1. polling (the client periodically asks for information)
2. long polling (the client asks for information and the server holds onto the request until information arrives or a timeout occurs)
3. sockets (the client requests server connection that is used for bi-directional communication for a period of time).
Knowing those terms, your next choice is to write your own or use a third-party service (azure, amazon, other) to deliver messages for you. I personally like long polling because it is easy to implement. In my application, I have the following setup.
A web API server on Azure with and endpoint that listens for message requests
A simple loop inside the server code that checks the database for new messages every 100ms.
A client that calls the API, handling the response.
As mentioned, there are many ways to do this. In your particular case, one way would be as follows.
Client A calls server API to listen for message
Server holds onto call, waiting for new message entry in database
Client B calls server API to post new message
Server saves message to database
Server instance from step 2 sees new message
Server returns message to Client A.
Also, the message doesn't have to be stored in a database - it just depends on your needs.
Sounds like you want to track users à la https://www.simple-talk.com/dotnet/asp.net/tracking-online-users-with-signalr/ , but in a desktop app in the sense of http://www.codeproject.com/Articles/804770/Implementing-SignalR-in-Desktop-Applications or damienbod.wordpress.com/2013/11/20/signalr-a-complete-wpf-client-using-mvvm/ .
The project I'm working on is a client-server application with all services written in WCF and the client in WPF. There are cases where the server needs to push information to the client. I initially though about using WCF Duplex Services, but after doing some research online, I figured a lot of people are avoiding it for many reasons.
The next thing I thought about was having the client create a host connection, so that the server could use that to make a service call to the client. The problem however, is that the application is deployed over the internet, so that approach requires configuring the firewall to allow incoming traffic and since most of the users are regular users, that might also require configuring the router to allow port forwarding, which again is a hassle for the user.
My third option is that in the client, spawns a background thread which makes a call to the GetNotifications() method on server. This method on the server side then, blocks until an actual notification is created, then the thread is notified (using an AutoResetEvent object maybe?) and the information gets sent to the client. The idea is something like this:
Client
private void InitializeListener()
{
Task.Factory.StartNew(() =>
{
while (true)
{
var notification = server.GetNotifications();
// Display the notification.
}
}, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
Server
public NotificationObject GetNotifications()
{
while (true)
{
notificationEvent.WaitOne();
return someNotificationObject;
}
}
private void NotificationCreated()
{
// Inform the client of this event.
notificationEvent.Set();
}
In this case, NotificationCreated() is a callback method called when the server needs to send information to the client.
What do you think about this approach? Is this scalable at all?
For each client you are going to hold a thread on the server. If you have a few hundred clients and the server wouldn't use the memory anyway, that may be fine. If there can be more clients, or you do not wish to burn 1MB of stack per client, you should make some changes:
Use an async WCF action method. They allow you to unblock the request thread while the method is waiting.
Change the event model to an async once. SemaphoreSlim has async support. You can also use TaskCompletionSource.
That way you can scale up to many connections.
I have a remote WCF web service that I'm connecting to from my application.
The application may be running on a server with multiple IP addresses (or multiple physical network interfaces)
I need to make sure that I can control which IP address is being used for the outbound request, instead of just using the 'preferred' interface as per the normal metric rules.
The reason for this is that multiple copies of the software will be running on the same machine, each bound to a specific IP address for its own operations, and the remote service being connected to needs to know which one is being used to connect back to it at a later time (since getting the address wrong means connecting to the wrong service)
With legacy ASMX services this is done by overriding GetWebRequest(Uri uri) on the partial class generated for the service. But I cannot figure out at all how to do this with WCF.
On an unrelated SO post, MVP #JohnSaunders suggested this may be possible by taking over the entire transport mechanism used by WCF. But I've not yet figured out how to do this either.
This is a tricky problem, which WCF doesn't seem to cater for particularly well.
The only component in the .NET framework that seems to directly deal with the issue of the client address is the ServicePoint class. Specifically, it has a BindIPEndPointDelegate property which lets you control how it selects the client IP. The documentation for the property includes this:
Some load balancing techniques require a client to use a specific local IP address and port number, rather than IPAddress.Any (or IPAddress.IPv6Any for Internet Protocol Version 6) and an ephemeral port. Your BindIPEndPointDelegate can satisfy this requirement.
Thus, you should be able to modify the service point associated with your URL in code like this:
var servicePoint = ServicePointManager.FindServicePoint(
new Uri("http://contoso.com/service.svc"));
servicePoint.BindIPEndPointDelegate =
(sp, remote, retryCount) => new IPEndPoint(address, portNumber);
Obviously this kind of code requires your classes have awareness of the protocol and the endpoint address the client will be communicating with. It would likely be most appropriate to set up this logic as a client behaviour which can be applied to your client channel.
you can use a message property (HttpRequestMessageProperty) to add HTTP headers to any outgoing requests. You need to create a "scope" in which the property will be added to the current "operation context", and attach the property, with all headers you want, to the outgoing message properties of the context.
please look at this :
how-to-override-getwebrequest-method-using-service-reference-instead-of-web-reference-in-wcf
Use this:
New Class:
using System.Web.Services.Protocols;
using System.Windows.Forms;
using System;
public static class ExtensionMethods
{
public static string ApplyServerURL(this SoapHttpClientProtocol service)
{
try
{
string name = service.GetType().Name;
return string.Format("{0}{1}.svc", Settings.Instance.ServerAddress, name);
}
catch
{ return string.Empty; }
}
}
And now is something like this:
YourService us = new YourService();
us.Url = us.ApplyServerURL();
I need such scenario: client sends message to server, not waiting for response, and don't care, if message was send properly.
using(host.RemoteService client = new host.RemoteService())
{
client.Open();
cliend.SendMessage("msg");
}
in scenario when firewall is on, or there is no connection to the internet, client dies at "SendMessage". I mean program stops to respond. I wish program don't care about the result. I mean if there is no connection, i wish program to go further, omitting "SendMessage" or sth like that.
What should I do, is there any solution for non blocking method?
Try something like this in your service contract:
[OperationContract(IsOneWay=true)]
void Send(string message);
See the following link:
One Way Operation in WCF
Edit: OP was already using my suggested solution.
Suggested approaches to solve the issue - taken from MSDN (One-Way Services):
Clients Blocking with One-Way Operations
It is important to realize that while some one-way applications return
as soon as the outbound data is written to the network connection, in
several scenarios the implementation of a binding or of a service can
cause a WCF client to block using one-way operations. In WCF client
applications, the WCF client object does not return until the outbound
data has been written to the network connection. This is true for
all message exchange patterns, including one-way operations; this
means that any problem writing the data to the transport prevents the
client from returning. Depending upon the problem, the result could
be an exception or a delay in sending messages to the service.
You can mitigate some of this problem by inserting a buffer between
the client object and the client transport's send operation. For
example, using asynchronous calls or using an in-memory message
queue can enable the client object to return quickly. Both
approaches may increase functionality, but the size of the thread pool
and the message queue still enforce limits.
It is recommended, instead, that you examine the various controls on
the service as well as on the client, and then test your application
scenarios to determine the best configuration on either side. For
example, if the use of sessions is blocking the processing of messages
on your service, you can set the
System.ServiceModel.ServiceBehaviorAttribute.InstanceContextMode
property to PerCall so that each message can be processed by a
different service instance, and set the ConcurrencyMode to
Multiple in order to allow more than one thread to dispatch messages
at a time. Another approach is to increase the read quotas of the
service and client bindings.
Modify your attribute
[OperationContract(IsOneWay=true)]