1

我很难自己解决这个问题,因此在这里问。我有一个在远程机器上安装客户端的应用程序,客户端通过套接字返回报告。

我试图创建一个保持计时器。这意味着客户端将每 1 分钟向服务器发送一个 hello 消息。如果服务器在保持计时器内没有收到来自客户端的 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(); 并且循环在收到的 hello 数据包的 dict 中找到引用重置该计时器。?

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 秒,我会考虑让客户端断开连接。

4

2 回答 2

7

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.

于 2013-05-16T12:27:08.240 回答
2

如果我理解了您的问题,我会怎么做:

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

您可以使用 a ConcurrentDictionaryfor 集合,轻松编写线程安全代码。

于 2013-05-16T11:44:38.753 回答