保持定时器类型线程

时间:2013-05-16 11:16:54

标签: c# multithreading timer system.reactive

我很难自己解决这个因此在这里问。 我有一个应用程序,它在远程计算机上安装客户端 并且客户端通过套接字报告。

我试图创建一个保持计时器。这意味着客户端将每1分钟向服务器发送一个问候消息。如果服务器在保持定时器内没有从客户端获取hello数据包,则客户端将被删除。 我可以和一个客户做得很好...... 但对于多个我无法弄明白。 我正在考虑创建一个线程foreach连接到服务器的新客户端。然后在该线程内部启动一个保持计时器..但我如何区分客户端与线程,以及如何重置保持计时器如果从客户端收到一个hello数据包。

我想为每个hello数据包创建一个新线程并停止旧数据包。 但我不知道这将是如何cpu密集

如果你不理解我的问题,请说出来,并试着解释你不理解的任何事情。

哪种解决方案更好? 解决方案A) 每当hello数据包到达时,启动一个新线程并停止旧线程? (每40秒) - 来自100个客户

解决方案B) 在这里插入更好的可扩展解决方案。

解决方案C) 我可以在动态创建的线程中访问计时器吗? - 我在所有动态创建的线程上都有唯一的线程名称。

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

当我从客户端收到一个与线程同名的hello数据包时重置它?

解决方案D) 将计时器存储在字典中   public Dictionary timersDict = new Dictionary(); 并且循环在dict中找到关于hello数据包接收重置该定时器的引用。?

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

设置远程主机应用程序,每隔3秒向服务器发送一条“hello”消息。 在服务器上创建一个计时器,每1秒发生一次经过的事件。 设置一个静态值来保存一个int,它决定了,在我认为远程机器死了多久之前,我选择了9秒。

当服务器收到hello消息时,它会将时间戳存储在列表中,并显示客户端的IP地址。

在timer_elapsed事件

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

        }
    }

基本上我会查看连接的客户端列表,如果上次收到的hello消息超过9秒,我认为客户端已断开连接。

2 个答案:

答案 0 :(得分:7)

这是Reactive Extensions的完美应用程序。这是一个快速的应用程序示例,您可以尝试(添加Nuget包Rx-Main)。它通过要求您为其ID输入整数来模拟客户端。在看到特定ID后经过timeToHold后,将运行Subscribe中的操作。

使用Synchronize很重要 - 从RX版本2开始,没有同步调用,Subject OnNext不是线程安全的。

多亏了RX,这个解决方案在使用定时器和线程方面非常有效。

注意:我在我的博客上添加了更详细的解释: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();
}

附录:哦,如果您的服务器负责启动客户端,您可能希望在启动客户端时从服务器发送OnNext,以防客户端从不发送心跳。

答案 1 :(得分:2)

如果我理解你的问题,我会做什么:

  • 创建一个唯一的集合,存储客户端的标识符,关联的套接字对象(或放弃连接所需的任何内容),以及上次收到的'hello'的日期
  • 让一个线程(或一个计时器)每隔几百毫秒迭代一次,检查日期并删除过时的连接
  • 当客户端发送'hello'时,在集合中找到相应的条目,并更新相关日期。

您可以使用ConcurrentDictionary进行集合,以轻松编写线程安全的代码。