将异步Dns.GetHostEntry结果写入集合 [英] Writing asynchronous Dns.GetHostEntry results to a collection

查看:112
本文介绍了将异步Dns.GetHostEntry结果写入集合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

你好.
我正在编写具有排序查看功能的IP数据包嗅探器.它包含两种形式-一种主要形式,负责收集数据并实时显示,另一种形式(StatisticsForm),可以从菜单选项中打开.

StatisticsForm的控制器在打开时会在所有IP地址上执行Dns.GetHostEntry的异步版本,由嗅探模块收集,然后将新识别的控制器添加到StatisticForm模型中的Dictionary<string,IPAddress>中. />

Hello.
I''m writing an IP packet sniffer with sorted viewing functionality. It contains two forms- the main form, responsible for gathering data and showing it in real time, and a second one (StatisticsForm), that can be opened from a menu option.

The StatisticsForm''s controller, while being opened, performs an asynchronous version of Dns.GetHostEntry on all IP adresses, gathered by the sniffing module, and adds the newly recognized ones to a Dictionary<string,IPAddress> in StatisticForm''s model.

public void ResolveNewHosts()
{
    foreach (IPPacket packet in _packetModel.PacketsReceived)
    {
        if (!(_model.Hostnames.Values.Contains(packet.Header.SourceAddress)))
            IdentifyHost(packet.Header.SourceAddress);
        if (!(_model.Hostnames.Values.Contains(packet.Header.DestinationAddress)))
            IdentifyHost(packet.Header.DestinationAddress);
    }
}







public void IdentifyHost(IPAddress ip)
{
    Dns.BeginGetHostEntry(ip, new AsyncCallback(OnResolve), null);
    UpdatelblRHState(true);
}

private void OnResolve(IAsyncResult ar)
{
    try
    {
        IPHostEntry host = Dns.EndGetHostEntry(ar);
        KeyValuePair<String, IPAddress> hostKVP;
        if (host.AddressList.Count() == 0)
            hostKVP = new KeyValuePair<String, IPAddress>(host.HostName, _packetModel.LocalHost);    //yeah, I know this is faulty logic here ;)
        else
            hostKVP = new KeyValuePair<String, IPAddress>(host.HostName, host.AddressList[0]);
        _model.AddHostname(hostKVP);      //Here the host adding occurs
        UpdatelblRHState(false);
    }
    catch (SocketException)
    {
    }
}



_model.AddHostname(hostKVP)方法如下所示:



The _model.AddHostname(hostKVP) method looks like this:

public void AddHostname(KeyValuePair<String, IPAddress> hostname)
{
    if (!_hostnames.ContainsKey(hostname.Key))
    {
        _hostnames.Add(hostname.Key, hostname.Value);
        NotifyObservers();
    }
}



NotifyObservers()导致StatisticsForm运行Revise()方法,从而导致执行此操作:



NotifyObservers() causes the StatisticsForm to run a Revise() method, causing it to do this:

public void Revise(IModel reporter)
{
    if (reporter is HostStorage)
    {   
/*the below conditional is a desperate attempt for a cure- doesn't really work.*/
        if (cbHostnames.Items.Count < _model.Hostnames.Count)
        {
            cbHostnames.DataSource = new BindingSource(_model.Hostnames, null);
            cbHostnames.DisplayMember = "Key";
            cbHostnames.ValueMember = "Value";
        }
    }
        /*...*/
        /*non-essential code cut out for clarity*/
}



现在,我的问题似乎是,在线程之间发生了争执,它们随时随需使用OnResolve回调弹出,然后尝试修改Dictionary.
这有两个作用:

1)集合已修改;枚举操作可能无法执行."尝试执行cbHostnames.DataSource = new BindingSource...
时出错

OR (当我逐步执行F10时会发生这种情况)

2)每个线程在其尝试添加新条目的内存中(当完成时会忘记该内存)都有一个不同的Hostnames集合.因此,通常仅主线程能够正确执行,导致例如添加了2个主机名,而不是5个.

由于我对多线程的经验还不太丰富,您能为我提供一些有关我的问题的具体帮助或指导吗?

P.S.我尝试在执行OnResolve的整个时间内锁定_model.Hostnames集合-不起作用.同样在控制器类中创建private readonly Object locker并将其锁定在OnResolve中不会产生任何结果.



Now what my problems seems to be is that i have a race between the threads which pop up with the OnResolve callback anytime they want, and then they try to modify the Dictionary.
There are two effects of this:

1) "Collection was modified; enumeration operation may not execute." error while trying to execute cbHostnames.DataSource = new BindingSource...

OR (and this occurs when I go through the process step-by-step with F10)

2) Every thread has a different Hostnames collection in it''s memory (which gets forgotten by the time it finishes) to which it tries to add new entries. Therefore, usually only the main thread is able to execute properly, resulting in e.g. 2 hostnames added instead of 5.

As I''m not too experienced with multi-threading yet, would you provide me with some specific help or guidance with my problem?

P.S. I have tried locking the _model.Hostnames collection for the entire time of OnResolve execution- didn''t work. Also creating a private readonly Object locker in the controller class and locking that in OnResolve didn''t yield any results.

推荐答案

UI调用应编组到主控制器上线. NotifyObservers被钩住做UI工作,因此您需要在某个时候做.也许UI类的事件处理程序已经在执行此操作了?

但是,这里的问题更为根本.读取集合以创建UI列表在主线程中进行.写入集合发生在后台线程中.因此,它们会发生碰撞,如您所见.

线程安全字典在.Net Framework中有点痛.从编码角度来看,最简单的答案(可能会满足您的目的)是锁定(model.Hostnames)以编写和枚举.

一个更高级的解决方案是通知观察者有新行添加,而不仅仅是某些情况已更改".如果您(i)使用_hostnames [key] = value,而不是Add,则将DNS查找添加到字典中是安全的操作,因此如果您覆盖,则不会得到异常,并且(ii)DNS查找将返回相同的内容每次(如果事实并非如此,那么您还是有问题).因此,如果您将AddHostname中的新行通知给观察者,而不是使用索引分配Add,并且让监视表单将新条目添加到其列表中,而不是完全刷新,则无需锁定.使用此方法,您可以获取同一主机的多个通知,因此观察者表单在添加之前应检查(在其本地列表中).
UI calls should be marshalled onto the main thread. NotifyObservers is being hooked to do UI work, so you will need to do that at some point. Perhaps the UI classes'' event handlers are already doing that?

The problem here is more fundamental, though. Reading the collection, to create the UI list, occurs in the main thread. Writing to the collection occurs in background threads. Therefore they can collide, as you are seeing.

Threadsafe Dictionary is a bit of a sore spot in the .Net Framework. The simple (from a coding perspective) answer which will probably serve your purposes is to lock(model.Hostnames) for writing and enumerating.

A more advanced solution would be to notify the observers of new row adds, not just that ''something has changed''. Adding DNS lookups to the dictionary is a safe operation if you (i) use _hostnames[key] = value, not Add, so you don''t get exceptions if you overwrite, and (ii) the DNS lookup will return the same thing each time (if that''s not true you have issues anyway). So if you notify observers of the new row in AddHostname, use index assignment not Add, and have the watching form add the new entry to its list instead of completely refreshing, you shouldn''t need to lock. Using this method you can get multiple notifications of the same host, so the watcher form should check for that (in its local list) before adding.


这篇关于将异步Dns.GetHostEntry结果写入集合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆