浪歌 · 随笔 我的开发笔记

C# WinForm实现局域网扫描和端口检测的示例代码

[.net] 2024/12/5 17:27:15

using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace LANScanner
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        private async void btnScan_Click(object sender, EventArgs e)
        {
            // 清空之前的结果
            listBoxResults.Items.Clear();

            // 获取本机IP和子网掩码
            NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
            foreach (NetworkInterface network in networkInterfaces)
            {
                if (network.OperationalStatus == OperationalStatus.Up)
                {
                    IPInterfaceProperties properties = network.GetIPProperties();
                    foreach (UnicastIPAddressInformation ip in properties.UnicastAddresses)
                    {
                        if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                        {
                            string localIP = ip.Address.ToString();
                            int subnetMaskPrefix = ip.PrefixLength;

                            // 开始扫描
                            await ScanLocalNetwork(localIP, subnetMaskPrefix);
                            return;
                        }
                    }
                }
            }
        }

        private async Task ScanLocalNetwork(string localIP, int subnetMaskPrefix)
        {
            // 获取子网前缀
            string subnetPrefix = GetSubnetPrefix(localIP, subnetMaskPrefix);

            // 要扫描的端口
            int[] portsToScan = new int[] { 8080, 80, 8000, 9000 }; // 根据你的服务端端口添加

            // 并行扫描
            ConcurrentBag<string> validAddresses = new ConcurrentBag<string>();

            await Task.Run(() =>
            {
                Parallel.For(1, 255, new ParallelOptions { MaxDegreeOfParallelism = 50 }, (i) =>
                {
                    string ip = $"{subnetPrefix}{i}";
                    
                    // 跳过本机IP
                    if (ip == localIP) return;

                    foreach (int port in portsToScan)
                    {
                        if (CheckPort(ip, port))
                        {
                            validAddresses.Add($"{ip}:{port}");
                            break; // 找到一个可用端口就停止
                        }
                    }
                });
            });

            // 更新UI
            foreach (string address in validAddresses)
            {
                listBoxResults.Invoke(new Action(() =>
                {
                    listBoxResults.Items.Add(address);
                }));
            }
        }

        private string GetSubnetPrefix(string ip, int subnetMaskPrefix)
        {
            // 获取子网前缀,例如 192.168.1.
            string[] parts = ip.Split('.');
            return $"{parts[0]}.{parts[1]}.{parts[2]}.";
        }

        private bool CheckPort(string ip, int port)
        {
            try
            {
                using (TcpClient client = new TcpClient())
                {
                    // 设置较短的超时时间,提高扫描速度
                    client.ConnectTimeout = 500;
                    client.Connect(ip, port);
                    return true;
                }
            }
            catch
            {
                return false;
            }
        }
    }

    // 扩展TcpClient,增加连接超时
    public static class TcpClientExtensions
    {
        public static void ConnectTimeout(this TcpClient client, string host, int port, int timeoutMilliseconds)
        {
            var result = client.BeginConnect(host, port, null, null);
            bool success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(timeoutMilliseconds));
            
            if (success)
            {
                client.EndConnect(result);
            }
            else
            {
                client.Close();
                throw new Exception("Connection timeout");
            }
        }
    }

}

这段代码实现了局域网IP和端口扫描的功能,主要特点:

  1. 自动获取本机IP和子网
  2. 并行扫描局域网内的IP
  3. 支持多个端口扫描
  4. 使用较短的连接超时,提高扫描效率
  5. 将可用的IP和端口显示在ListBox中

使用说明:

  • 在WinForm设计器中添加一个按钮(btnScan)和一个ListBox(listBoxResults)
  • 点击"扫描"按钮开始扫描
  • 扫描结果会显示在ListBox中,格式为 "IP:端口"

注意事项:

  • portsToScan数组中添加你服务端实际使用的端口
  • 扫描速度和准确性受网络环境影响
  • 大型局域网可能需要调整并行度和超时时间

建议优化:

  1. 添加进度条
  2. 允许用户自定义端口范围
  3. 增加更详细的错误处理