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());
}
相关推荐
_OP_CHEN7 分钟前
从零开始的Qt开发指南:(五)Qt 常用控件之 QWidget(上):解锁 Qt 界面开发的核心基石
开发语言·c++·qt·前端开发·qwidget·gui开发·qt常用控件
wjs202413 分钟前
SQLite 视图
开发语言
q***448120 分钟前
java进阶--多线程学习
java·开发语言·学习
艾斯比的日常25 分钟前
Neo4j 完全指南:从核心特性到 Java 实战(附企业级应用场景)
java·开发语言·neo4j
唐青枫34 分钟前
C#.NET 范围与索引(Range、Index)完全解析:语法、用法与最佳实践
c#·.net
后端小张35 分钟前
【JAVA 进阶】深入探秘Netty之Reactor模型:从理论到实战
java·开发语言·网络·spring boot·spring·reactor·netty
柠石榴36 分钟前
go-1 模型
开发语言·后端·golang
laocooon5238578862 小时前
实现了一个新闻数据采集与分析系统python
linux·服务器·windows
q***d1732 小时前
Rust在网络中的协议栈
开发语言·网络·rust
星释2 小时前
Rust 练习册 88:OCR Numbers与光学字符识别
开发语言·后端·rust