VisualBean
VisualBean

Reputation: 5008

Hold timer type thread

im having difficulties figuring this out for myself hence asking here. i have an application which installs a client on a remote machine and the client reports back through a socket.

im trying to create a hold timer. which means the client will send a hello message every 1 minute to the server.. if the server doesnt get a hello packet from the client within the hold timer the client is removed. i can do it fine with one client... but for multiple i cannot figure it out. i was thinking to create a thread foreach new client that connected to the server. and then inside that thread start a hold timer.. but how do i distinguish clients from one another with the threads and how do i reset the hold timer IF a hello packet is recieved from a client.

i thought of creating a new thread for every hello packet and stopping the old one. but i dont know how cpu intensive this will be

if you dont understand my question, please say so and ill try to explain whatever you dont understand.

which solution is better? Solution A) starting a new thread and stopping the old thread everytime a hello packet arrives? (every 40 secs) - from 100 clients

Solution B) insert much better more scalable solution here.

Solution C) can i access a timer inside a thread i create dynamically? - i have unique threadnames on all dynamically created threads.

public void _clientholdtimer()
    {          
    holdtimer = new Timer();
    holdtimer.Interval = SECtoMS(Settings.Default.Hold);
    holdtimer.Elapsed += holdtimer_Elapsed;          
    }

and reset it when i recieve a hello packet from client with same name as thread?

Solution D) store the timers in a dictionary public Dictionary timersDict = new Dictionary(); and the loop find the reference in the dict on hello packet recieved reset that timer.?

foreach(var item in timersDict)
                            {
                                if(item.Key == stripip(client.RemoteEndPoint.ToString()))
                                    TimerReset(item.Value);
                            }   

SOLUTION

Setup up the remote host application, to send a "hello" message to the Server every 3 seconds. Create a timer on the server with an elapsed event hit every 1 sec. setup a static value to hold an int which determines, how long before I consider the remote machine dead, I chose 9 seconds.

when the server receives a hello message it stores the timestamp in a list among with the ip address of the client.

on the timer_elapsed event

void holdtimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {

        try
        {
            if (server.Clients.GetAllItems().Count != 0)
            {
                foreach (IScsServerClient _client in server.Clients.GetAllItems())
                {
                    string ipaddr = stripip(_client.RemoteEndPoint.ToString());
                    bool containsentry = _clientlist.Any(item => item.ipadress == ipaddr);
                    if (containsentry)
                    {
                        foreach (Scsserverclient entry in _clientlist.Where(entry => entry.ipadress == ipaddr))
                        {
                            if ((DateTime.Now - TimeSpan.FromSeconds(Settings.Default.Hold)) > entry.lasthello &&
                                entry.isConnect != "Disconnected")
                            {
                                entry.client.Disconnect();                              
                            }
                        }
                    }                                             
                }

            }
        }
        catch (Exception ex)
        {

        }
    }

basically i run through a list of connected clients and if the last received hello message is older than 9 seconds, i consider to client to disconnected.

Upvotes: 1

Views: 488

Answers (2)

James World
James World

Reputation: 29786

This is a perfect application for Reactive Extensions. Here's a quick consle-app example you can try (add Nuget package Rx-Main). It simulates clients by asking you to input an integer for their ID. After timeToHold has elapsed since a particular ID has been seen, the action in Subscribe is run.

The use of Synchronize is important - as of RX version 2 Subject OnNext is not threadsafe without the Synchronize call.

Thanks to RX, this solution is very efficient in how it uses timers and threads.

Note: I've added a much more detailed explanation for this on my blog here: http://www.zerobugbuild.com/?p=230

public void Main()
{
    var clientHeartbeats = new Subject<int>();

    var timeToHold = TimeSpan.FromSeconds(5);

    var expiredClients = clientHeartbeats
        .Synchronize()
        .GroupBy(key => key)
        .SelectMany(grp => grp.Throttle(timeToHold));

    var subscription = expiredClients.Subscribe(
        // in here put your disconnect action
        i => Console.WriteLine("Disconnect Client: " + i));

    while(true)
    {
        var num = Console.ReadLine();
        if (num == "q")
        {
            break;
        }
   // call OnNext with the client id each time they send hello
        clientHeartbeats.OnNext(int.Parse(num));
    }   

    // if you need to tear down you can do this
    subscription.Dispose();
}

Addendum: Oh, if your server is responsible for starting clients, you may want to send an OnNext in from the server when it starts a client in case the client never sends a heartbeat.

Upvotes: 7

Kevin Gosse
Kevin Gosse

Reputation: 39007

What I would do, if I've understood your issue:

  • Create a unique collection, storing the identifier of the clients, the associated socket object (or whatever you need to drop the connection), and the date of the last received 'hello'
  • Have a single thread (or a timer) iterate through the collection every few hundred milliseconds, checking the dates and dropping the obsolete connections
  • When the client sends a 'hello', find the appropriate entry in the collection, and update the associated date.

You can use a ConcurrentDictionary for the collection, to easily write thread-safe code.

Upvotes: 2

Related Questions