Best way to mark Client Offline using Web-Service - c#

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)

Related

Unexpected error in windows service

I've got a WCF windows service running. It basically registers a number of clients and broadcasts messages to them as and when needed.
Generally it works fine, but recently I've been getting an error trying to register a new client, there are about 24 clients connected already, but when I try to register a 25th, I get "Index was outside the bounds of the array." being returned.
If I restart the service, all the clients reconnect, and the new client is able to register.
The NotifyServer method is called to broadcast a message to all the registered clients. This runs through the clients dictionary creating an async task that sends the message. This is done so that any issue happening with sending to one client does not impact the sending of the message to others.
The service is set up as to use a Reliable Connection.
// List of connected Clients
//
private static Dictionary<string, IBroadcastorCallBack> clients = new Dictionary<string, IBroadcastorCallBack>();
// lock indicator object
//
private static object locker = new object();
public string RegisterClient(string clientName)
{
string returnValue = "";
if (!string.IsNullOrEmpty(clientName))
{
try
{
var callback = OperationContext.Current.GetCallbackChannel<IBroadcastorCallBack>();
lock (locker)
{
// Remove the old client if its already there
//
if (clients.Keys.Contains(clientName))
{
clients.Remove(clientName);
}
// Add the new one
//
clients.Add(clientName, callback);
}
}
catch (Exception ex)
{
// Return any error message
//
returnValue = ex.Message;
}
}
return returnValue;
}
/// <summary>
/// Notify the service of a message that can be broadcast
/// </summary>
/// <param name="eventData">Message details</param>
///
public async void NotifyServer(EventDataType eventData)
{
DateTime Start = DateTime.Now;
// Get a list copy of the dictionary for all clients bar the one sending it. This is so we can't update the list inside the loop.
//
var clientlist = clients.Where(x => x.Key != eventData.ClientName).ToList();
// Logging
//
if (MetricBroadcast.Properties.Settings.Default.LogXML)
{
log.Debug("XML Broadcast from " + eventData.ClientName + " to " + clientlist.Count.ToString() + " clients:\n" + eventData.EventMessage + "\n");
}
// Broadcast to all the valid clients
//
var BroadcastToClientList = clientlist.Select(client => BroadcastMessage(client.Value, client.Key, eventData)).ToList();
// Wait until they are all done
//
await Task.WhenAll(BroadcastToClientList);
// If we are logging and the broadcast time is > 1 second, we make a log entry
//
DateTime End = DateTime.Now;
if (MetricBroadcast.Properties.Settings.Default.LogXML)
{
TimeSpan res = End - Start;
if (res.TotalSeconds > 1)
{
var timetaken = string.Format("XML Broadcast Time : {0,2:00}:{1,2:00}.{2,3:000}", res.Minutes, res.Seconds, res.Milliseconds);
log.Debug(timetaken);
}
}
}
private async Task<bool> BroadcastMessage(IBroadcastorCallBack clientCallback, string ClientKey, EventDataType eventData)
{
bool retval = true;
Exception savedEx = null;
DateTime BroadStart = DateTime.Now;
try
{
// Send the message to the current client
//
clientCallback.BroadcastToClient(eventData);
}
catch (Exception e)
{
// If we can't access the current clients callback method,
// we remove them from the clients list, as they've probably lost their connection.
//
clients.Remove(ClientKey);
savedEx = e;
retval = false;
}
// Log any broadcast that took > .5 seconds
//
DateTime BroadEnd = DateTime.Now;
if (MetricBroadcast.Properties.Settings.Default.LogXML)
{
TimeSpan res = BroadEnd - BroadStart;
if (res.TotalSeconds > .5)
{
var timetaken = string.Format("Single XML Broadcast Time to " + ClientKey + " : {0,2:00}:{1,2:00}.{2,3:000}", res.Minutes, res.Seconds, res.Milliseconds);
log.Debug(timetaken, savedEx);
}
}
return retval;
}
Most likely the problem is incorrect usage of static clients Dictionary in multithreaded environment. New client can register at any time, including in the middle of your BroadcastMessage and NotifyServer functions. Dictionary was not designed for access from multiple threads. Take for example this:
clients.Where(x => x.Key != eventData.ClientName).ToList();
What happens if client is removed in the middle of this enumeration? Who knows, because it was not designed for this, but most likely some exception (like your "index out of bounds") will be thrown. That is why you need to lock your dictionary for both writes and reads, not just for writes:
List<KeyValuePair<string, IBroadcastorCallback>> clientList;
lock (locker)
clientList = clients.Where(x => x.Key != eventData.ClientName).ToList();
Another option is to use ConcurrentDictionary class. It was designed for concurrent access and you can safely read and write to it from multiple threads.

How to stop the timer at OnDisconnected event in signalR

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).

Client not receiving data from Server Multithreading

I want to make a chat. The server is made in console app and the client is made in winforms.
In client I write a nickname and connect to server. The server receives name from client. I add all clients that connect to server in a Dictionary list with the (string)name and (TcpClient)Socket. After, I want to send to every client the client list.
When I debug on server, the Sockets appear with DualMode,EnableBroadcast error. In client when I have to receive the list it stops and doesn't do anything.
Server
namespace MyServer
{
class MyServer
{
public Dictionary<string, TcpClient> clientList = new Dictionary<string, TcpClient>();
TcpListener server = null;
NetworkStream stream = null;
StreamReader streamReader = null;
StreamWriter streamWriter = null;
TcpClient clientSocket;
String messageReceived;
int number_clients = 0;
public MyServer(TcpClient clientSocket_connect)
{
stream = clientSocket_connect.GetStream();
streamReader = new StreamReader(stream);
streamWriter = new StreamWriter(stream);
receiveMessage(clientSocket_connect); // receive messages
}
public MyServer()
{
Thread thread = new Thread(new ThreadStart(run));
thread.Start();
}
public void receiveMessage(TcpClient client_Socket)
{
messageReceived = streamReader.ReadLine();
if (messageReceived.Substring(messageReceived.Length - 4) == "user")
{
String name = messageReceived.Substring(0, messageReceived.Length - 4);
bool found = false;
foreach (var namefound in clientList.Keys)
{
if (namefound == name)
{
found = true;
streamWriter.WriteLine("The user already exists");
streamWriter.Flush();
}
}
if (!found)
{
//show who's connected
Console.WriteLine(name + " is online");
number_clients++;
clientList.Add(name, client_Socket);
//send to client clientlist
String send = null;
foreach (var key in clientList.Keys)
{
send += key + ".";
}
foreach (var value in clientList.Values)
{
TcpClient trimitereclientSocket = value;
if (trimitereclientSocket != null)
{
NetworkStream networkStream = trimitereclientSocket.GetStream();
StreamWriter networkWriter = new StreamWriter(networkStream);
networkWriter.WriteLine(send + "connected");
networkWriter.Flush();
}
}
}
}
}
void run()
{
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
server = new TcpListener(ipAddress, 8000);
server.Start();
Console.WriteLine("Server started!");
while (true)
{
clientSocket = server.AcceptTcpClient();
new MyServer(clientSocket);
}
}
}
static void Main(string[] args)
{
MyServer server = new MyServer();
}
}
Client
namespace MyClient
{
class MyClient
{
List<string> clientList = new List<string>();
TcpClient client = null;
NetworkStream stream = nul
l;
StreamReader streamReader = null;
StreamWriter streamWriter = null;
bool connected;
String received_message;
public MyClient()
{
client = new TcpClient("127.0.0.1", 8000);
stream = client.GetStream();
streamReader = new StreamReader(stream);
streamWriter = new StreamWriter(stream);
}
public void sendClientName(String name)
{
streamWriter.WriteLine(Convert.ToString(name));
streamWriter.Flush();
}
public List<ClientName> receiveClientList()
{
List<ClientName> val = new List<ClientName>();
string name = Convert.ToString(streamReader.ReadLine());
if (name.Substring(0, name.Length - 9) == "connected")
{
ClientName client = new ClientName();
client.Nume = name;
val.Add(client);
}
return val;
}
}
}
Client Form
public partial class Form1 : Form
{
MyClient client = new MyClient();
public Form1()
{
InitializeComponent();
Thread receiveClients = new Thread(new ThreadStart(getMessages));
}
private void btnConnect_Click(object sender, EventArgs e)
{
client.sendClientName(txtNickname.Text + "user");
}
public void getMessages()
{
while (true)
{
lbClientsConnected.Items.Add(client.receiveClientList());
}
}
}
I was unable to reproduce any error when running your code. I don't know what you mean by "the Sockets appear with DualMode,EnableBroadcast error". That said, there are a number of fixable problems with the code, including some that pertain directly to your concern that "when I have to receive the list it stops and doesn't do anything."
Probably the biggest issue with the code is that you simply never start the client's receiving thread. You need to call the Start() method on the Thread object after it's been created:
public Form1()
{
InitializeComponent();
Thread receiveClients = new Thread(new ThreadStart(getMessages));
// The receiving thread needs to be started
receiveClients.Start();
}
Now, even with that fixed, you have a few other problems. The next big issue is that you are parsing the received text incorrectly. In your code, where you should be looking for the text "connected" at the end of the string, you instead extract the other part of the text (with the list of client names).
Your receiveClientList() method should instead look like this:
private const string _kconnected = "connected";
public List<string> receiveClientList()
{
List<string> val = new List<string>();
string name = Convert.ToString(streamReader.ReadLine());
// Need to check the *end* of the string for "connected" text,
// not the beginning.
if (name.EndsWith(_kconnected))
{
name = name.Substring(0, name.Length - _kconnected.Length);
val.Add(name);
}
return val;
}
(You didn't share the ClientName class in your question, and really the example doesn't need it; a simple string value suffices for the purpose of this exercise. ALso, I've introduced the const string named _kconnected, to ensure that the string literal is used correctly in each place it's needed, as well as to simplify usage.)
But even with those two issues fixed, you've still got a couple in the Form code where you actually handle the return value of the receive method. First, you are passing the List<T> object that is returned from the receive method to the ListBox.Items.Add() method, which would just result in the ListBox displaying the type name for the object, rather than its elements.
Second, because the code is executing in a thread other than the UI thread that owns the ListBox object, you must wrap the call in a call to Control.Invoke(). Otherwise, you'll get a cross-thread operation exception.
Fixing those two issues, you get this:
public void getMessages()
{
while (true)
{
// Need to receive the data, and the call Invoke() to add the
// data to the ListBox. Also, if adding a List<T>, need to call
// AddRange(), not Add().
string[] receivedClientList = client.receiveClientList().ToArray();
Invoke((MethodInvoker)(() => listBox1.Items.AddRange(receivedClientList)));
}
With those changes, the code will process the message sent by the client, and return the list of clients. That should get you further along. That said, you still have a number of other problems, including some fairly fundamental ones:
The biggest issue is that when you accept a connection in the server, you create a whole new server object to handle that connection. There are a number of reasons this isn't a good idea, but the main one is that the rest of the code seems to conceptually assume that a single server object is tracking all of the clients, but each connection will result in its own collection of client objects, each collection having just one member (i.e. that client).
Note that once you've fixed this issue, you will have multiple threads all accessing a single dictionary data structure. You will need to learn how to use the lock statement to ensure safe shared use of the dictionary across multiple threads.
Another significant problem is that instead of using the streamWriter you created when you first accepted the connection, you create a whole new StreamWriter object (referenced in a local variable named networkWriter) to write to the socket. In this very simple example, it works fine, but between buffering and the lack of thread safety, this incorrectly-designed code could have serious data corruption problems.
Less problematic, but worth fixing, is that your server code completely fails to take advantage of the fact that you're storing the clients in a dictionary, as well as that .NET has useful helper functions for doing things like joining a bunch of strings together. I would write your server's receiveMessage() method something more like this:
private const string _kuser = "user";
public void receiveMessage(TcpClient client_Socket)
{
messageReceived = streamReader.ReadLine();
if (messageReceived.EndsWith(_kuser))
{
String name = messageReceived.Substring(0, messageReceived.Length - _kuser.Length);
if (clientList.ContainsKey(name))
{
streamWriter.WriteLine("The user already exists");
streamWriter.Flush();
return;
}
//show who's connected
Console.WriteLine(name + " is online");
number_clients++;
clientList.Add(name, client_Socket);
string send = string.Join(".", clientList.Keys);
foreach (var value in clientList.Values.Where(v => v != null))
{
// NOTE: I didn't change the problem noted in #2 above, instead just
// left the code the way you had it, mostly. Of course, in a fully
// corrected version of the code, your dictionary would contain not
// just `TcpClient` objects, but some client-specific object specific
// to your server implementation, in which the `TcpClient` object
// is found, along with the `StreamReader` and `StreamWriter` objects
// you've already created for that connection (and any other per-client
// data that you need to track). Then you would write to that already-
// existing `StreamWriter` object instead of creating a new one each
// time here.
NetworkStream networkStream = value.GetStream();
StreamWriter networkWriter = new StreamWriter(networkStream);
networkWriter.WriteLine(send + "connected");
networkWriter.Flush();
}
}
}
The above is not exhaustive by any means. Frankly, you probably should spend more time looking at existing examples of network-aware code, e.g. on MSDN and Stack Overflow, as well as on tutorials on web sites, blogs, or in books. Even when you write the server in a one-thread-per-connection way as you seem to be trying to do here, there are lots of little details you really need to get correct, and which you haven't so far.
But I do hope the above is enough to get you past your current hurdle, and on to the next big problem(s). :)

Capture the start and end time of calling the server from the client in .NET application

How can i on the client side pass a parameter that gets the start and end time which is how long it takes to call this call to the server since the server stores some information and it cannot communicate directly to the client but the client can request. I need to get the shortest, longest, and average time and have that information show up in the UI in the background client where i can go in and see how long it takes each user to call the server.
#region Timer Handlers
private void OnTimer(object sender, ElapsedEventArgs e)
{
Ping();
}
protected virtual void Ping()
{
try
{
//Created Date Time to
DateTime _Now = DateTime.Now;
var incomingRequests = GetInboundEcounterInfoForClientCommand.CheckForNewInboundRequests(
_sessionKey, _userName, _machineName, _openedPatients);
DateTime NowAgain = DateTime.Now;
TimeSpan difference = NowAgain - _Now;
PaceartLoggingService.LogInfo()
if (incomingRequests != null)
{
incomingRequests.ForEach(request => _requests.Enqueue(request));
if (_requests.Count > 0)
{
InvokeInboundDataAvailible();
}
}
}
catch (Exception ex) // this should only happen when the server goes down
{
PaceartLoggingService.LogError(typeof(GetInboundEcounterInfoForClientCommand), ex);
}
}
private void InvokeInboundDataAvailible()
{
if (InboundDataAvailible != null)
{
InboundDataAvailible(this, EventArgs.Empty);
}
}
#endregion
I did not completely get your question's point, I assume you want to measure the execution time of a long-time-running method. The method is on server and you call it on Client.
There are two places you can do so.
First approach: you can start a timer, call the method, get the result, stop the counter, and now you have the execution time according to client. This approach is not very much accurate because you are also counting the connection time, I mean if the method takes 10 secs and the connection takes 2 secs, you get 12 seconds.
Second approach: you can use the same timer on server method. You call the server method from client, the server starts the timer, does the long-time-job, stops the timer, and finally passes back the time elapsed as the result to the client. This approach is a little more accurate because connection time will not be counted.
public class Client
{
public void CallServerMethod_ApproachOne()
{
Server s = new Server();
var sw = System.Diagnostics.Stopwatch.StartNew();
s.LongLongTimeMethod_ApproachOne();
sw.Stop();
MessageBox.Show(sw.ElapsedMilliseconds.ToString());
}
public void CallServerMethod_ApproachTwo()
{
Server s = new Server();
var elapsedMilliseconds = s.LongLongTimeMethod_ApproachTwo();
MessageBox.Show(elapsedMilliseconds.ToString());
}
}
public class Server
{
public void LongLongTimeMethod_ApproachOne()
{
//this is just a way to waste time, your real work is done here
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(new Random().Next(1, 10)));
return;
}
public long LongLongTimeMethod_ApproachTwo()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(new Random().Next(1, 10)));
sw.Stop();
return sw.ElapsedMilliseconds;
}
}
And for logging the shortest, longest and average time, you can keep the values in a sort of list or array.
Last of all, using DateTime to get the execution time is not an appropriate solution. The best solution is to use Profilers, you can google it.
Good luck

Email me if windows service stops

I want to be able to check if a certain service is running (say it had a Display name - ServiceA). I want my program to check say every 5 mins that the service is still running. If it is fine, it will loop and wait another 5 mins and then check again. If it finds that ServiceA has stopped I want the program to email me and say...ServiceA has stopped running. Below I have the code which I have done so far which is able to pull all the current services running and there actual display name back to the console. Anyone any ideas on the code/logic needed for what I need above?
namespace ServicesChecker
{
class Program
{
static void Main(string[] args)
{
ServiceController[] scServices;
scServices = ServiceController.GetServices();
Console.WriteLine("Services running on the local computer:");
foreach (ServiceController scTemp in scServices)
{
if (scTemp.Status == ServiceControllerStatus.Running)
{
Console.WriteLine();
Console.WriteLine(" Service : {0}", scTemp.ServiceName);
Console.WriteLine(" Display name: {0}", scTemp.DisplayName);
}
}
//Create a Pause....
Console.ReadLine();
}
}
}
Put every service's name in an array and check if your wanted name is running
List<string> arr = new List<string>();
foreach (ServiceController scTemp in scServices)
{
if (scTemp.Status == ServiceControllerStatus.Running)
{
arr.add(scTemp.ServiceName);
}
}
if (arr.Contains("YourWantedName")
{
// loop again
}
else
{
// send mail
}
There's no need to iterate over all services, if you know which one you're looking for: you can instantiate ServiceController with the service name.
As for sending an email: take a look at the System.Net.Mail.MailMessage class.
NB: You know that you can also just configure the service to trigger an action if it fails?
You will need to track the state of the service which will require some sort of storage. The simplest is probably an XML file that tracks the status of the service, maybe a schema like this
<services>
<service name="service1" last-check="12/21/2011 13:00:05" last-status="running" />
...
</services>
Your monitoring app, will wake up find the status of the services it is interested in, and check to see what that service's previous status was. If the status was running, but is currently stopped, send the email. If the service wasn't found add it to the list of services.
Persisting the status of the services to disk protects you in the case when your monitoring app goes down.
Heres an example of a service which does quite something similar. Should be simple to adapt it to your needs..
public partial class CrowdCodeService : ServiceBase
{
private Timer stateTimer;
private TimerCallback timerDelegate;
AutoResetEvent autoEvent = new AutoResetEvent(false);
public CrowdCodeService()
{
InitializeComponent();
}
int secondsDefault = 30;
int secondsIncrementError = 30;
int secondesMaximum = 600;
int seconds;
protected override void OnStart(string[] args)
{
Loggy.Add("Starting CrowdCodeService.");
timerDelegate = new TimerCallback(DoSomething);
seconds = secondsDefault;
stateTimer = new Timer(timerDelegate, autoEvent, 0, seconds * 1000);
}
static bool isRunning = false;
// The state object is necessary for a TimerCallback.
public void DoSomething(object stateObject)
{
if (CrowdCodeService.isRunning)
{
return;
}
CrowdCodeService.isRunning = true;
AutoResetEvent autoEvent = (AutoResetEvent)stateObject;
try
{
////// Do your work here
string cs = "Application";
EventLog elog = new EventLog();
if (!EventLog.SourceExists(cs))
{
EventLog.CreateEventSource(cs, cs);
}
elog.Source = cs;
elog.EnableRaisingEvents = true;
elog.WriteEntry("CrowdCodes Service Error:" + cmd.Message.ToString(), EventLogEntryType.Error, 991);
}
}
finally
{
CrowdCodeService.isRunning = false;
}
}
protected override void OnStop()
{
Loggy.Add("Stopped CrowdCodeService.");
stateTimer.Dispose();
}
}

Categories

Resources