i have following scenario. I am running a tcp client where i get push notifications like heartbeats and other objects. I can also run commands against the server like GetWeather infos or something like that. Everytime i receive an
object i raise an event which works pretty fine.
But now i want to be able to request some data on the server and wait for it until the server responses the right object. During the request of an object other objects can also be send to me.
Here is some pseudocode:
Instead of:
TcpServer.ObjectReceived += ObjectReceivedMethod;
TcpServer.GetWeather();
public void ObjectReceived(object data)
{
}
I want:
var result = await TcpServer.GetWeather();
How can i transfer the Weather Info from ObjectReceived to the awaiting method?
KR Manuel
You want to use a TaskCompletionSource<T>, something like this:
private Dictionary<Guid, TaskCompletionSource<WeatherResponse>> _weatherRequests;
public Task<WeatherResponse> GetWeatherAsync()
{
var messageId = Guid.NewGuid();
var tcs = new TaskCompletionSource<WeatherResponse>();
_weatherRequests.Add(messageId, tcs);
_server.SendWeatherRequest(messageId);
return tcs.Task;
}
public void ObjectReceived(object data)
{
...
if (data is ServerWeatherResponse)
{
var tcs = _weatherRequests[data.requestId];
_weatherRequests.Remove(data.requestId);
tcs.SetResult(new WeatherResponse(data));
}
}
This assumes that your server will associate requests with responses using a GUID id.
Related
I have a MQTT calls inside a loop and in each iteration, it should return a response from the subscriber so that I could use the value being forwarded after I published. But the problem is I don't know how would I do it.
I hope you have an idea there or maybe if I'm just not implementing it right, may you guide me through this. Thanks.
Here's my code:
// MyClientMgr
class MyClientMgr{
public long CurrentOutput { get; set; }
public void GetCurrentOutput(MyObjectParameters parameters, MqttClient client)
{
MyMessageObject msg = new MyMessageObject
{
Action = MyEnum.GetOutput,
Data = JsonConvert.SerializeObject(parameters)
}
mq_GetCurrentOutput(msg, client);
}
private void mq_GetCurrentOutput(MyMessageObject msg, MqttClient client)
{
string msgStr = JsonConvert.SerializeObject(msg);
client.Publish("getOutput", Encoding.UTF8.GetBytes(msgStr),
MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
client.MqttMsgPublishReceived += (sender, e) =>{
MyObjectOutput output = JsonConvert.DeserializeObject<MyObjectOutput>(Encoding.UTF8.GetString(e.Message));
CurrentOutput = output;
};
}
}
// MyServerMgr
class MyServerMgr
{
public void InitSubscriptions()
{
mq_GetOutput();
}
private void mq_GetOutput()
{
MqttClient clientSubscribe = new MqttClient(host);
string clientId = Guid.NewGuid().ToString();
clientSubscribe.Connect(clientId);
clientSubscribe.Subscribe(new string[] { "getOutput" }, new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });
MqttClient clientPublish = new MqttClient(host);
string clientIdPub = Guid.NewGuid().ToString();
clientPublish.Connect(clientIdPub);
clientSubscribe.MqttMsgPublishReceived += (sender, e) => {
MyMessageObj msg = JsonConvert.DeserializeObject<MyMessageObj>(Encoding.UTF8.GetString(e.Message));
var output = msg.Output;
clientPublish.Publish("getOutput", Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(output)), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
}
}
}
// MyCallerClass
class MyCallerClass
{
var host = "test.mqtt.org";
var myServer = new MyServerMgr(host);
var myClient = new MyClientMgr();
myServer.InitSubscriptions();
MqttClient client = new MqttClient(host);
for(int i = 0; i < 10; i++)
{
long output = 0;
MyObjectParameters parameters = {};
myClient.GetCurrentOutput(parameters, client) // here I call the method from my client manager
// to publish the getting of the output and assigned
// below for use, but the problem is the value doesn't
// being passed to the output variable because it is not
// yet returned by the server.
// Is there a way I could wait the process to
// get the response before assigning the output?
output = myClient.CurrentOutput; // output here will always be null
// because the response is not yet forwarded by the server
}
}
I have a loop in my caller class to call the mqtt publish for getting the output, but I have no idea how to get the output before it was assigned, I want to wait for the response first before going to the next.
I've already tried doing a while loop inside like this:
while(output == 0)
{
output = myClient.CurrentOutput;
}
Yes, I can get the output here, but it will slow down the process that much. And sometimes it will fail.
Please help me. Thanks.
It looks like you are trying to do synchronous communication over an asynchronous protocol (MQTT).
By this I mean you want to send a message and then wait for a response, this is not how MQTT works as there is no concept of a reply to a message at the protocol level.
I'm not that familiar with C# so I'll just give an abstract description of possible solution.
My suggestion would be to use a publishing thread, wait/pulse (Look at the Monitor class) to have this block after each publish and have the message handler call pulse when it has received the response.
If the response doesn't contain a wait to identify the original request you will also need a state machine variable to record which request is in progress.
You may want to look at putting a time out on the wait in case the other end does not respond for some reasons.
You can use AutoResetEvent class that has WaitOne() and Set() methods. Using WaitOne() after publish will wait until the message is published and using Set() under client_MqttMsgPublishReceived event will release the wait when the subscriber received the message he subscribed for.
How can I get a list of opened connections from SignalR without sending them a "ping" or manual registering them into own list?
UPDATE:
Or just to get number of clients to which a message was sent from a hub. I want to know how many responses I should wait for (without waiting whole timeout).
(Since, SignalR does not support return values when calling clients from a hub, I am collecting results by another message that clients are sending to the hub.)
CLARIFICATION: I assume SignalR must know to which connections is sending a message.
You can store user's id onConnected and remove it on disconnect. See this is only an example using a database to persist the connected ids
protected Task OnConnected(IRequest request, string connectionId){
var context=new dbContext();
context.Connections.Add(connectionId);
context.Save();
}
protected Task OnDisconnected(IRequest request, string connectionId){
var context=new dbContext();
var id=context.Connections.FirstOrDefault(e=>e.connectionId==connectionId);
context.Connections.Remove(id);
context.Save();
}
then everywhere you need to access the list of connected ids, you request your db.
I haven't yet found any direct way of doing that.
The best I have come up so far is following the tutorial - USERS BY CONNECTIONS IN SIGNALR, you can find more code in the link, I have simplified it for basic understanding.
public void Register(HubClientPayload payload, string connectionId)
{
lock (_lock)
{
List<String> connections;
if (_registeredClients.TryGetValue(payload.UniqueID, out connections))
{
if (!connections.Any(connection => connectionID == connection))
{
connections.Add(connectionId);
}
}
else
{
_registeredClients[payload.UniqueID] = new List<string> { connectionId };
}
}
}
and
public Task Disconnect(string connectionId)
{
lock (_lock)
{
var connections = _registeredClients.FirstOrDefault(c => c.Value.Any(connection => connection == connectionId));
// if we are tracking a client with this connection remove it
if (!CollectionUtil.IsNullOrEmpty(connections.Value))
{
connections.Value.Remove(connectionId);
// if there are no connections for the client, remove the client from the tracking dictionary
if (CollectionUtil.IsNullOrEmpty(connections.Value))
{
_registeredClients.Remove(connections.Key);
}
}
return null;
}
also,
public Task Reconnect(string connectionId)
{
Context.Clients[connectionId].reRegister();
return null;
}
When i send a request to my server or a reply to my client, the message i send is always divided into multiple parts.
So i need to call Receive multiple times to get to the last part/frame.
Why does this happen.. is there a better way of sending and receiving an xml encoded string?
This is the code of my client:
private void SendRequestAsyncTaskStart<T>(object contextObj, T request)
{
ZmqContext context = (ZmqContext)contextObj;
ZmqSocket requestSocket = CreateServerSocket(context);
SerializeAndSendRequest(request, requestSocket);
}
private ZmqSocket CreateServerSocket(ZmqContext context)
{
var client = context.CreateSocket(SocketType.REQ);
client.Connect(_requestReplyEndpoint);
client.Linger = TimeSpan.Zero;
client.ReceiveReady += PollInReplyHandler;
return client;
}
public static string Serialize(this object obj)
{
string result;
using (var memoryStream = new MemoryStream())
{
using (var reader = new StreamReader(memoryStream))
{
var serializer = new DataContractSerializer(obj.GetType());
serializer.WriteObject(memoryStream, obj);
memoryStream.Position = 0;
result = reader.ReadToEnd();
}
}
return result;
}
This is the code of my server:
private void ListenForRequestsThreadStart(object contextObj)
{
ZmqContext context = (ZmqContext)contextObj;
using (
ZmqSocket frontend = context.CreateSocket(SocketType.REP),
backend = context.CreateSocket(SocketType.DEALER))
{
string bindAddress = string.Format("tcp://*:{0}", _listenForRequetsPort);
frontend.Bind(bindAddress);
backend.Bind("inproc://backend");
frontend.ReceiveReady += HandleRequestReceived;
// polling
}
}
private void HandleRequestReceived(object sender, SocketEventArgs e)
{
string message;
bool hasNext;
do
{
message = socket.Receive(Encoding.ASCII);
hasNext = socket.ReceiveMore;
} while (hasNext);
// after calling Receive 3 times i get my actual message
}
Since you're sending via a socket you're at the mercy of the network. First, the network will have broken your message down in multiple packates each of which is received separately by your listener. Every now and then, the underlying socket on the listening machine will say to itself 'Got some incoming, but there's more to come. Wait a bit'. After a while it'll say, 'Oh well, give what I've got' and keep waiting'.
That's what's happening. In WCF, the WCF implementation gets its data via sockets which do exactly the same thing. But WCF waits till the whole message arrives before giving it to your waiting code. That's one of the advantages of using a Framework like WCF. It protects you from the metal.
Any message sent over TCP may be divided into several packets depending on its size. That's why you should never assume to get a message in one go, but read until you're sure you've received everything.
I have an API in which i do a lot of processing like sending a service bus queue message and receiving it, adding entries to table and then finally send an event to socket.io server. I want all this to be protected by concurrent executions. I am using Lock, but it doesnt seem to do the trick. Am i missing anything? below is my code
public class BroadcastController : ApiController
{
private readonly Object _Lock = new Object();
[HttpPost]
[ActionName("UploadRecording")]
public async Task<HttpResponseMessage> UploadRecording()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string path = Path.GetTempPath();
var provider = new MultipartFormDataStreamProvider(path);
// Read the form data and return an async task.
var response = await Request.Content.ReadAsMultipartAsync(provider);
// processing the mime content
lock (_Lock)
{
// sending and receiving service bus messages
// adding records to table
// sending an event to socket.io
return Request.CreateResponse(HttpStatusCode.OK, new ResponseMessage<PersistedAudioRecord> { SuccessCode = 1, Message = "Broadcast Uploaded", Data = updatedRecord });
} } }
Make the _Lock object static. Otherwise, you are using a different lock for each instance of the controller. Since the framework creates a new instance of the controller to process each request, each request is locking on a different _Lock object, thus providing no concurrency safety.
However, note that even a static lock object will only work if you have a single server. If you have multiple servers handling requests you'll need to manage concurrency by another means.
I'm developing a communication API to be used by a lot of generic clients to communicate with a proprietary system.
This proprietary system exposes an API, and I use a particular classes to send and wait messages from this system: obviously the system alert me that a message is ready using an event. The event is named OnMessageArrived.
My idea is to expose a simple SendSyncMessage(message) method that helps the user/client to simply send a message and the method returns the response.
The client:
using ( Communicator c = new Communicator() )
{
response = c.SendSync(message);
}
The communicator class is done in this way:
public class Communicator : IDisposable
{
// Proprietary system object
ExternalSystem c;
String currentRespone;
Guid currentGUID;
private readonly ManualResetEvent _manualResetEvent;
private ManualResetEvent _manualResetEvent2;
String systemName = "system";
String ServerName = "server";
public Communicator()
{
_manualResetEvent = new ManualResetEvent(false);
//This methods are from the proprietary system API
c = SystemInstance.CreateInstance();
c.Connect(systemName , ServerName);
}
private void ConnectionStarter( object data )
{
c.OnMessageArrivedEvent += c_OnMessageArrivedEvent;
_manualResetEvent.WaitOne();
c.OnMessageArrivedEvent-= c_OnMessageArrivedEvent;
}
public String SendSync( String Message )
{
Thread _internalThread = new Thread(ConnectionStarter);
_internalThread.Start(c);
_manualResetEvent2 = new ManualResetEvent(false);
String toRet;
int messageID;
currentGUID = Guid.NewGuid();
c.SendMessage(Message, "Request", currentGUID.ToString());
_manualResetEvent2.WaitOne();
toRet = currentRespone;
return toRet;
}
void c_OnMessageArrivedEvent( int Id, string root, string guid, int TimeOut, out int ReturnCode )
{
if ( !guid.Equals(currentGUID.ToString()) )
{
_manualResetEvent2.Set();
ReturnCode = 0;
return;
}
object newMessage;
c.FetchMessage(Id, 7, out newMessage);
currentRespone = newMessage.ToString();
ReturnCode = 0;
_manualResetEvent2.Set();
}
}
I'm really noob in using waithandle, but my idea was to create an instance that sends the message and waits for an event. As soon as the event arrived, checks if the message is the one I expect (checking the unique guid), otherwise continues to wait for the next event.
This because could be (and usually is in this way) a lot of clients working concurrently, and I want them to work parallel.
As I implemented my stuff, at the moment if I run client 1, client 2 and client 3, client 2 starts sending message as soon as client 1 has finished, and client 3 as client 2 has finished: not what I'm trying to do.
Can you help me to fix my code and get my target?
Thanks!
autoResetEvent - controls a main connection life cycle. I do not see where you release this handle by calling Set() so OnMessageArrived event is unsubscribed
autoResetEvent2 - controls incomming messages, you shall set this event only if a message with expected GUID is received, basically just
if (guid == currentGUI.ToString())
{
autoResetEvent2.Set();
}
Also use more clear and descriptive names for variables so it will be easier to write and understand a code