C#实现多线程局域网扫描器的思路与具体代码

C#实现多线程局域网扫描器的思路与具体代码

思路:

  1. 获取局域网内所有 IP 地址
  2. 遍历所有 IP 地址,使用 Ping 命令测试主机是否在线
  3. 如果主机在线,则扫描主机上的所有端口,确定哪些端口是开放的
  4. 输出扫描结果

在上述过程中,第 2 步和第 3 步都可以使用多线程来加速。具体来说,可以将 IP 地址分成若干段,每个线程负责扫描一段 IP 地址。对于每个 IP 地址,也可以启动一个线程来扫描其端口。

具体代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading;

namespace LANScanner
{
    class Program
    {
        // 扫描参数
        static int numThreads = 100;
        static int timeout = 100;

        static void Main(string[] args)
        {
            // 获取本地 IP 地址和子网掩码
            IPAddress[] addresses = Dns.GetHostAddresses(Dns.GetHostName());
            IPAddress subnetMask = null;
            foreach (NetworkInterface adapter in NetworkInterface.GetAllNetworkInterfaces())
            {
                IPInterfaceProperties properties = adapter.GetIPProperties();
                foreach (UnicastIPAddressInformation addressInfo in properties.UnicastAddresses)
                {
                    if (addressInfo.Address.AddressFamily == AddressFamily.InterNetwork)
                    {
                        subnetMask = addressInfo.IPv4Mask;
                        break;
                    }
                }
                if (subnetMask != null)
                {
                    break;
                }
            }

            // 计算子网地址和广播地址
            IPAddress subnetAddress = GetSubnetAddress(addresses[0], subnetMask);
            IPAddress broadcastAddress = GetBroadcastAddress(addresses[0], subnetMask);

            // 创建线程池并开始扫描
            List<WaitHandle> handles = new List<WaitHandle>();
            ThreadPool.SetMinThreads(numThreads, numThreads);
            for (int i = 1; i <= 254; i++)
            {
                IPAddress ip = GetIPAddress(subnetAddress, i);
                handles.Add(new ManualResetEvent(false));
                ThreadPool.QueueUserWorkItem(PingHost, new object[] { ip, handles.Last() });
            }
            WaitHandle.WaitAll(handles.ToArray());

            Console.WriteLine("Scanning complete.");
            Console.ReadLine();
        }

        static void PingHost(object args)
        {
            // 解析参数
            object[] parameters = (object[])args;
            IPAddress ip = (IPAddress)parameters[0];
            ManualResetEvent handle = (ManualResetEvent)parameters[1];

            // 测试主机是否在线
            Ping ping = new Ping();
            PingReply reply = ping.Send(ip, timeout);
            if (reply.Status == IPStatus.Success)
            {
                Console.WriteLine("Host {0} is online.", ip);

                // 扫描主机上的端口
                List<WaitHandle> handles = new List<WaitHandle>();
                for (int port = 1; port <= 65535; port++)
                {
                    handles.Add(new ManualResetEvent(false));
                    ThreadPool.QueueUserWorkItem(CheckPort, new object[] { ip, port, handles.Last() });
                }
                WaitHandle.WaitAll(handles.ToArray());
            }

            // 通知主线程完成
            handle.Set();
        }

        static void CheckPort(object args)
        {
            // 解析参数
            object[] parameters = (object[])args;
            IPAddress ip = (IPAddress)parameters[0];
            int port = (int)parameters[1];
            ManualResetEvent handle = (ManualResetEvent)parameters[2];

            // 尝试连接端口
            try
            {
                using (TcpClient client = new TcpClient())
                {
                    client.Connect(ip, port);
                    Console.WriteLine("Port {0} is open on host {1}.", port, ip);
                }
            }
            catch (Exception)
            {
                // 端口未打开
            }

            // 通知主线程完成
            handle.Set();
        }

        static IPAddress GetSubnetAddress(IPAddress address, IPAddress subnetMask)
        {
            byte[] addressBytes = address.GetAddressBytes();
            byte[] maskBytes = subnetMask.GetAddressBytes();
            byte[] subnetBytes = new byte[4];
            for (int i = 0; i < 4; i++)
            {
                subnetBytes[i] = (byte)(addressBytes[i] & maskBytes[i]);
            }
            return new IPAddress(subnetBytes);
        }

        static IPAddress GetBroadcastAddress(IPAddress address, IPAddress subnetMask)
        {
            byte[] addressBytes = address.GetAddressBytes();
            byte[] maskBytes = subnetMask.GetAddressBytes();
            byte[] broadcastBytes = new byte[4];
            for (int i = 0; i < 4; i++)
            {
                broadcastBytes[i] = (byte)(addressBytes[i] | ~maskBytes[i]);
            }
            return new IPAddress(broadcastBytes);
        }

        static IPAddress GetIPAddress(IPAddress subnetAddress, int host)
        {
            byte[] subnetBytes = subnetAddress.GetAddressBytes();
            byte[] hostBytes = new byte[4];
            hostBytes[3] = (byte)host;
            byte[] ipBytes = new byte[4];
            for (int i = 0; i < 4; i++)
            {
                ipBytes[i] = (byte)(subnetBytes[i] | hostBytes[i]);
            }
            return new IPAddress(ipBytes);
        }
    }
}

上述代码实现了一个简单的多线程局域网扫描器,支持 Ping 主机和扫描主机端口,并输出扫描结果。需要注意的是,在实际应用中,可能需要对输入进行严格的验证和过滤,以确保系统安全。

同时,在使用多线程和异步 I/O 操作时,也需要注意程序的正确性和健壮性。如果不正确地使用这些技术,可能会导致程序出现各种问题,例如竞态条件和死锁。因此,在使用这些技术时,请务必小心并仔细测试程序。

要解决错误“WaitHandles的数量必须小于或等于64”,需要将句柄列表拆分为更小的块,并使用WaitAll分别等待每个块。例如,你可以将句柄列表分成4个块,每个块25个句柄,然后分别等待每个块:

List<WaitHandle>[] handleChunks = new List<WaitHandle>[4];
for (int i = 0; i < handleChunks.Length; i++)
{
    handleChunks[i] = new List<WaitHandle>();
}
for (int i = 1; i <= 254; i++)
{
    // ...
    int chunkIndex = (i - 1) % handleChunks.Length;
    handleChunks[chunkIndex].Add(new ManualResetEvent(false));
    ThreadPool.QueueUserWorkItem(PingHost, new object[] { ip, handleChunks[chunkIndex].Last() });
}
for (int i = 0; i < handleChunks.Length; i++)
{
    WaitHandle.WaitAll(handleChunks[i].ToArray());
}

猜你喜欢

转载自blog.csdn.net/Documentlv/article/details/132098107