How to stop the timer at OnDisconnected event in signalR - c#

I have started a timer when my hub connection is established in PushNotificationData method by clients requests.
As per timer interval, it does fetch the records from the db and push to the clients.
but when clients got disconnected ,this timer has to be stopped instead of pulling continuously.
So I have used OnDisconnected event to stop the timer. but unfortunately timer not getting stopped
here is my code:
public class NotifyHub : Hub
{
private string ConnectionId;
private int UserId;
private int UserTypeId;
Timer timer = new Timer();
public override Task OnConnected()
{
ConnectionId = Context.ConnectionId;
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
timer.Stop();
timer.Enabled = false;
//logic code removed for brevity
return base.OnDisconnected(stopCalled);
}
public void PushNotificationData(Int32 userId, Int16 userTypeId)
{
UserId = userId;
UserTypeId = userTypeId;
ConnectionId = Context.ConnectionId;
timer.Elapsed += Timer_Elapsed1;
timer.Interval = 6000;
timer.Enabled = true;
timer.Start();
}
private void Timer_Elapsed1(object sender, ElapsedEventArgs e)
{
var notificationParams = new PushNotificationRequest
{
FilterProperty = new Common.Filters.FilterProperty { Offset = 0, RecordLimit = 0, OrderBy = "datechecked desc" },
Filters = new List<Common.Filters.FilterObject> { new Common.Filters.FilterObject { LogicOperator = 0, ConditionOperator = 0, Function = 0, FieldName = "", FieldValue = "", FieldType = 0 } }
};
using (INotificationManager iNotifity = new NotificationManager())
{
var taskTimer = Task.Run(async () =>
{
var NotificationResult = iNotifity.GetPushNotificationData(notificationParams, UserId, UserTypeId);
//Sending the response data to all the clients based on ConnectionId through the client method NotificationToClient()
Clients.Client(ConnectionId).NotificationToClient(NotificationResult);
//Delaying by 6 seconds.
await Task.Delay(1000);
//}
});
}
}
}
When I debug it, it shows timer enabled=true even after OnDisconnected fires.
the moment OnDisconneted is executing , i can able to see that timer get updated enabled=false. after it come out from the OnDisconnected timer.enabledgot true again.

Read about the Hub Object Lifetime here. The important part is this
Because instances of the Hub class are transient, you can't use them to maintain state from one method call to the next. Each time the server receives a method call from a client, a new instance of your Hub class processes the message. To maintain state through multiple connections and method calls, use some other method such as a database, or a static variable on the Hub class, or a different class that does not derive from Hub. If you persist data in memory, using a method such as a static variable on the Hub class, the data will be lost when the app domain recycles.
You essentially creating a new timer every time a new Hub is created. So you land up with multiple timers all calling the Timer_Elapsed1 method. You could try making Timer static and keep track of the connection count. That way you could stop the timer when all clients are disconnected. Be aware though that even static variables are vulnerable to be lost if the app domain recycles (as pointed out in the docs above).

Related

HttpClient throws httprequest exception on network restore

I am using System.Net.Http.HttpClient to make postaysnc request. While request is in progress I unplug the network cable, receive HttpRequestException.
After some time plug the network cable again and make the postasync request, getting the HttpRequestException - sometimes i get the response server not available,sometimes timeout
Do i need to dispose the httpclient on exception and recreate when the request is made? How to make the query successful on network restore.
private async Task<string> GetServerResult()
{
try
{
var response = await myHttpClient.PostAsync("https://google.com", httpContent);
response.EnsureSuccessStatusCode();
}
catch (HttpRequestException ex)
{
throw new HttpRequestException(ex.Message, ex.InnerException);
}
}
As per your requirement, you have to change implement some sort of implementation in that case. My proposed solution is use to a caching mechanism at WCF Client and update it periodically.
The very simple implementation could be as: You have a very simple singleton class of and a periodic Timer fetches the data from your mentioned endpoint. It stores the last cached data so that you have a copy of the data and when the hits are failed you can configure a fallback mechanism for that. For instance you have an implementation like
//You single Cache class
public sealed class ClientCache
{
#region Singleton implementation
private static ClientCache _clientCache = new ClientCache();
private ClientCache()
{
}
public static ClientCache Instance => _clientCache;
#endregion
//Timer for syncing the data from Server
private Timer _timer;
//This data is the cached one
public string data = string.Empty;
internal void StartProcess()
{
//Initializing the timer
_timer = new Timer(TimeSpan.FromMinutes(1).TotalMilliseconds); //This timespan is configurable
//Assigning it an elapsed time event
_timer.Elapsed += async (e, args) => await SyncServerData(e, args);
//Starting the timer
_timer.Start();
}
//In this method you will request your server and fetch the latest copy of the data
//In case of failure you can maintain the history of the last disconnected server
private async Task ProcessingMethod(object sender, ElapsedEventArgs e)
{
//First we will stop the timer so that any other hit don't come in the mean while
timer.Stop();
//Call your api here
//Once the hit is completed or failed
//On Success you will be updating the Data object
//data = result from your api call
//Finally start the time again as
timer.Start();
}
}
Now coming to Step two where to initialize the ClientCache Class. The best options are to initialize it in Global.asax class
protected void Application_Start()
{
//As
ClientCache.Instance.StartProcess();
}
Now whenever your frontend calls the method you don't need to go back to the server. Just send back the result from your cache as:
private Task<string> GetServerResult()
{
return Task.FromResult(ClientCache.Instance.data);
}

.NET Client - Waiting for an MQTT response before proceeding to the next request

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 to make windows service request wait until the previous request is complete

I am working on a window services application and my window service will call one of the web services in certain intervals (for example 3 min). From the web service I will get data from a database and using that data I will send an email.
If I am having huge sets of rows in my db table it will take some time to send the mail. Here I have the problem: The window services send the first request and it will handle some set of records. So, while processing it by the web service, the window service sends another request to the web service before it has completed the first request.
Due to this, the web service gets the same records from db again and again whenever it receives a new request from the windows service.
Can any one suggest me how to lock the previous request until it completes its work or some other way to handle this situation?
Web Service call:
protected override void OnStart(string[] args)
{
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
timer.Interval = 180000;
timer.AutoReset = false;
timer.Enabled = true;
}
Inside Method
using (MailWebService call = new MailWebService())
{
try
{
call.ServiceUrl = GetWebServiceUrl();
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
call.CheckMailQueue();
}
catch (Exception ex)
{
LogHelper.LogWriter(ex);
}
finally
{
}
}
The Monitor class works great for this scenario. Here is an example of how to use it:
// This is the object that we lock to control access
private static object _intervalSync = new object();
private void OnElapsedTime(object sender, ElapsedEventArgs e)
{
if (System.Threading.Monitor.TryEnter(_intervalSync))
{
try
{
// Your code here
}
finally
{
// Make sure Exit is always called
System.Threading.Monitor.Exit(_intervalSync);
}
}
else
{
//Previous interval is still in progress.
}
}
There is also an overload for TryEnter that allows you to specify timeout for entering the section.

execute a C# method asynchronously using Threading in windows service

i have a windows service that runs every 10 seconds to execute the read method. The Read method connects to remote server with the connection url provided in the constructor.
if the remote server fails to respond, it throws error and goes to catch. How do we make the thread to start again?
class PMTicketsService
{
private Timer _serviceTimer;
private TimerCallback _timerDelegate;
public PMTicketsService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
// Set the method to execute when the timer executes.
_timerDelegate = new TimerCallback(Receive);
// Create timer and attach our method delegate to it
_serviceTimer = new Timer(_timerDelegate, null, 1000, 10000);
}
public void Receive(object state)
{
ABC abc = new ABC(Url);
ABC abc1 = new ABC(Url1);
/* Create the thread object, passing in the abc.Read() method
via a ThreadStart delegate. This does not start the thread. */
Thread oThread = new Thread(new ThreadStart(abc.Read());
Thread oThread1 = new Thread(new ThreadStart(abc1.Read());
// Start the thread
oThread.Start();
oThread1.Start();
oThread.Join();
oThread1.Join();
}
}
class ABC
{
public string strUrl;
public ABC(string url)
{
strUrl = url;
}
public void Read()
{
try
{
// Code to use the connectionurl to access a remote server
}
catch (Exception ex)
{
// If the connection url fails to respond, how do we make thread start again?
}
}
}
In the future, you should submit sample code that actually compiles. I took what you had and cleaned it up, removed the unnecessary timer and structured it in a way that should give you what you need. In the code below, your Read method will continue running until you set done to true.
protected override void OnStart(string[] args)
{
try
{
ABC abc = new ABC("www.abc.com");
// Create the thread object, passing in the abc.Read() method
Thread oThread = new Thread(new ThreadStart(abc.Read));
// Start the thread
oThread.Start();
}
catch (Exception)
{
}
}
public class ABC
{
string strUrl = "";
public ABC(string url)
{
strUrl = url;
}
public void Read()
{
bool done = false;
while (!done)
{
try
{
//Code to use the connectionurl to access a remote server
//Use strUrl in here
}
catch (Exception)
{
//Wait 10 seconds before trying again
Thread.Sleep(10000);
}
//On success, set done to true
done = true;
}
}
}
Why do you want to start another thread? Starting/stopping threads is an expensive operation, you're far better off just keeping the existing thread open and continually trying to connect (possibly with a sleep in between). You already have the try/catch to keep the thread from crashing. Just wrap the try/catch in a while(!done) and set done to be true once you successfully connect.
You might also want to add some code so that if you can't connect X times in a row (maybe 5?) then you'll stop trying, or increase the timeout between connection attempts.

Best way to mark Client Offline using Web-Service

Currently I'm working with a Web-Service where Client's Continually should update Data .
So the WebService has a List<Client> clients; where it stores Connected Client's :
[WebMethod]
public string Connect(Size desktopsize)
{
Client clienti = new Client();
clienti.ID = "Client_" + Counter.ToString();
clienti.Desktopsize = desktopsize;
clienti.Lastupdate = DateTime.Now;
Counter++;
clients.Add(clienti);
return clienti.ID;
}
so every client has an ID ,and continue Updating it's Data's.
I need to mark a Client Offline ,than when the Last Update of a specific Client was 1 minute ago .(Im also updating the UpdateTime every time when a value is changed
like :
public bool SingleClick
{
get
{
bool tmpBolean = singleclick;
singleclick = false;
return tmpBolean;
}
set
{
this.lastupdate = DateTime.Now;
singleclick = value;
}
}
First i used to create a Thread at Client
private void CheckOnlinestate()
{
while (isRunning)
{
TimeSpan ts = DateTime.Now - lastupdate;
if (ts.TotalMinutes >= 1)
{
isRunning = false;
this.Dispose();
}
}
}
than at WebService a thread which monitor's if Client should remove from list:
public void CheckClients()
{
while (true)
{
foreach (Client c in clients)
{
if (c.ShouldDispose)
{
clients.Remove(c);
}
}
Thread.Sleep(200);
}
}
So the issue is ,how to use this method into a Thread Correctly ,should i Create and Start the thread at WebService Constructor ,or there is a better way to do that.
I cant imagine the best way how to remove a specific Client from List<Client> clients;
You can use System.Web.HttpRuntime.Cache class instead of implementing your own.
There are some other problems with your code. Accesses to Counter and clients are uncontrolled which are shared among threads. You should use some sync. mechanism such as lock while accessing these objects ( You can forget my comment about clients if you already declared it as ConcurrentDictionary)

Categories

Resources