解析主机名时防止异常

Preventing Exception when resolving Hostname

我正在尝试制作一个应用程序来扫描网络中的 ARP 请求并列出所有现有的网络设备。目前我使用 SharpPcapPacketDoNet.

根据给定的 IP 解析主机名时,解析 "unknown" 主机时得到 SocketException。所以我把它放在 try/catch 中。因为我认为这是不好的风格(忽略异常),所以我正在寻找不同的解决方案。

这是一些代码:

// Button for scanning the network
private void btnStartScanningForClients_Click(object sender, RoutedEventArgs e)
{
    // Check for correct interface
    // [...]

    // Start scanning process
    if (!this.netWorkItOut.Startet)
    {
        // Dis-/Enable visual controls
        // [...]

        // Start scanning
        var index = this.cbNetworkInterface.SelectedIndex
        this.netWorkItOut.StartDevice(index);
        this.netWorkItOut.Scanner.StartScanningNetwork(resolveHostnames);
    }
}

这是控制对象,它持有扫描仪、处理事件、接受数据包并将它们放入队列中

public void StartDevice(int deviceIndex)
{
    this.Startet = true;
    // [...]
    this.Device = WinPcapDeviceList.Instance[deviceIndex];

    // Activate Scanner
    this.Scanner = new Scanner(this.DeviceInfo);

    // Subscribe Events
    // [...]

    this.Device.Open(DeviceMode.Promiscuous, 1);
    this.Device.Filter = "(arp || ip || ip6)";

    this.Device.OnPacketArrival += device_OnPacketArrival;
    this.Device.StartCapture();
}

private void device_OnPacketArrival(object sender, CaptureEventArgs e)
{
    //PacketDoNet
    Packet packet;

    try
    { packet = Packet.ParsePacket(LinkLayers.Ethernet, e.Packet.Data); }
    catch (Exception)
    { return;  }

    if (packet is EthernetPacket)
    {
        var arp = ARPPacket.GetEncapsulated(packet);

        if (arp != null)
        {
            if (this.Scanner.Started)
            {
                lock (this.Scanner.PacketQueueARP)
                {
                    this.Scanner.PacketQueueARP.Add(arp);
                }
            }
        }
    }
}

这是控制对象和扫描仪class。扫描器 class 处理 ARP 请求并解析主机名

public void StartScanningNetwork(bool resolveHostnames)
{
    // [...]
    this.ResolveHostnames = resolveHostnames;

    // start worker to listen for ARP packets
    this.workerARP = new Thread(WorkerARP);
    this.workerARP.Name = "Scanner thread (ARP)";
    this.workerARP.Start();

    this.Started = true;
}

private void WorkerARP()
{
    List<IPAddress> processedIps = new List<IPAddress>();

    // copy packets from storage queue to thread queue for processing
    while (Started)
    {
        // [...]

        if (this.threadQueueARP.Count > 0)
        {
            foreach (var packet in this.threadQueueARP)
            {
                // [...]

                if (!processedIps.Contains(ip))
                {
                    // [...]

                    if (this.ResolveHostnames)
                    {
                        var resolveHostnamesTask = Task.Factory.StartNew(ResolveHostnamesWorker, ip);
                    }
                }
                // [...]
            }

            // [...]
        }
        // [...]
    }

}

private void ResolveHostnamesWorker(object data)
{
    if (data is IPAddress)
    {
        var ip = (IPAddress)data;
        var hostname = "";

        try
        {
            hostname = Dns.GetHostEntry(ip).HostName;
        }
        catch { }

        // Raise Event for hostname resolved
    }
}

都是关于hostname = Dns.GetHostEntry(ip).HostName

行的

因此:如何在通过 Dns.GetHostEntry() 解析 HostEntry 时避免使用 try/catch?如果没有已知主机,是否有 return 只是 null 的函数?

提前致谢!

据我所知,没有像 TryGetHostName() 这样不会抛出异常的方法。

但在我看来,按照您的预期捕获异常是很容易理解的。所以你应该限制捕获你期望的异常:

private void ResolveHostnamesWorker(object data)
{
    if (data is IPAddress)
    {
        var ip = (IPAddress)data;
        var hostname = "";

        try
        {
            hostname = Dns.GetHostEntry(ip).HostName;
        }
        catch(SocketException socketException)
        {
            // maybe limit handling based on data in socketException and
            // call throw; to rethrow exception if not the expected one
        }

    // Raise Event for hostname resolved
}

}

由于 Class Dns 的代码在 https://github.com/Microsoft/referencesource/blob/master/System/net/System/Net/DNS.cs 下可用,您只能从那里使用相关部分。 (当然你要检查你的项目是否可以使用MIT license)

实现在抛出异常的方法InternalGetHostByAddress中。如果查询成功,您可以在那里 return 一个值(bool、enum ...)来提供信息。