我自己很难弄清楚,所以在这里问。我有一个在远程机器上安装客户端的应用程序客户端通过套接字返回报告。
我试图创建一个保持定时器。这意味着客户端将每隔1分钟向服务器发送一个hello消息。如果服务器在保持定时器内没有收到客户端的hello报文,则客户端被移除。我一个客户就能搞定……但是对于倍数,我不能算出来。我正在考虑为连接到服务器的每个新客户端创建一个线程。然后在线程内部启动一个hold timer..但是我如何用线程区分客户端和另一个客户端,如果从客户端收到hello包,我如何重置保持定时器。
我想为每个hello包创建一个新线程并停止旧线程。但是我不知道这会占用多少CPU
如果你不明白我的问题,请说出来,我将尽力解释你不明白的地方。
哪个解决方案更好?解决方案一)每次收到hello包时,启动一个新线程并停止旧线程?(每40秒)-从100个客户端
解决方案B)在这里插入更好的、可扩展的解决方案。
解决方案C)我可以在动态创建的线程中访问计时器吗?-我为所有动态创建的线程设置了唯一的线程名。
public void _clientholdtimer()
{
holdtimer = new Timer();
holdtimer.Interval = SECtoMS(Settings.Default.Hold);
holdtimer.Elapsed += holdtimer_Elapsed;
}
和重置它,当我收到一个hello包从客户端与线程同名?
D)解决方案将计时器存储在字典中公共字典timerdict =新字典();并且循环在字典中找到对收到的hello包的引用,重置定时器。
foreach(var item in timersDict)
{
if(item.Key == stripip(client.RemoteEndPoint.ToString()))
TimerReset(item.Value);
}
<<p> 解决方案/strong> 设置远程主机应用程序,每3秒向服务器发送一个"hello"消息。在服务器上创建一个计时器,每1秒命中一个已经过的事件。设置一个静态值来保存一个int值,该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秒,我认为客户端已断开连接。
这是一个完美的响应式扩展应用程序。这里有一个快速的控制台应用程序示例,你可以尝试一下(添加Nuget包Rx-Main)。它通过要求您为其ID输入一个整数来模拟客户机。在timeToHold经过特定ID后,将运行Subscribe中的操作。
Synchronize的使用很重要——从RX版本2开始,如果没有Synchronize调用,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,以防客户端从不发送心跳
如果我明白你的问题,我会怎么做:
- 创建一个唯一的集合,存储客户端的标识符,相关的套接字对象(或任何你需要放弃连接),以及最后收到'hello'的日期
- 让一个线程(或计时器)每几百毫秒迭代一次集合,检查日期并丢弃过时的连接
- 当客户端发送'hello'时,在集合中找到相应的条目,并更新相关的日期。
您可以为集合使用ConcurrentDictionary
,以便轻松编写线程安全的代码。