| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Net;
- using System.Net.NetworkInformation;
- using System.Net.Sockets;
- using System.Runtime.CompilerServices;
- using System.Text;
- using System.Text.RegularExpressions;
- using System.Threading.Tasks;
- namespace TeamAAS.Feeder.Network
- {
- /// <summary>
- /// 网卡接口信息(IPv4)。
- /// </summary>
- public class NetworkInterfaceInfo
- {
- public string Name { get; set; }
- public IPAddress IPv4Address { get; set; }
- public IPAddress IPv4Mask { get; set; }
- }
- /// <summary>
- /// UDP 扫描发现的 Feeder 网络信息。
- /// </summary>
- public class FeederNetworkInfo : INotifyPropertyChanged
- {
- public string FeederType { get; set; }
- public string Version { get; set; }
- public string IP { get; set; }
- public int Port { get; set; }
- public string Subnet { get; set; }
- public string Gateway { get; set; }
- public string MAC { get; set; }
- private bool _isAccessible;
- /// <summary>是否与本机某网卡处于同一网段(前三段相同)。</summary>
- public bool IsAccessible
- {
- get { return _isAccessible; }
- set { SetProperty(ref _isAccessible, value); }
- }
- /// <summary>发现该 Feeder 所使用的本机网卡。</summary>
- public NetworkInterfaceInfo Network { get; set; }
- public override string ToString() => $"{FeederType} {IP}:{Port} ({MAC})";
- public event PropertyChangedEventHandler PropertyChanged;
- private void SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
- {
- if (EqualityComparer<T>.Default.Equals(storage, value)) return;
- storage = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
- }
- /// <summary>
- /// Feeder IP 自动搜索/设置(UDP 广播扫描)。
- /// 纯 System.Net.Sockets.UdpClient 实现,无第三方依赖。
- /// 协议:向 255.255.255.255:1234 广播 {udpscan},Feeder 回应
- /// { FF0Ver2.6.5 / ip:192.168.31.200:5000 / sub:255.255.255.0 / gw:... / mac:...}。
- /// </summary>
- public class FeederIpScanner
- {
- private const int BroadcastPort = 1234;
- private const int LocalBindPort = 8000;
- private const string LogSource = "Feeder.Scan";
- private UdpClient _udpClient = new UdpClient();
- /// <summary>扫描所有网卡,返回发现的 Feeder 列表。</summary>
- public async Task<List<FeederNetworkInfo>> ScanAsync()
- {
- return await Task.Run(() =>
- {
- var result = new List<FeederNetworkInfo>();
- try
- {
- var netList = GetAllNetworkInterface();
- foreach (var nic in netList)
- {
- Bind(nic.IPv4Address.ToString());
- var feeders = GetFeederList(nic);
- result.AddRange(feeders);
- }
- AppLogger.Info($"UDP 扫描完成,发现 {result.Count} 个 Feeder", LogSource);
- }
- catch (Exception ex)
- {
- AppLogger.Error("UDP 扫描 Feeder 失败", ex, LogSource);
- }
- return result;
- });
- }
- /// <summary>绑定到指定本机网卡 IP 与固定本地端口。</summary>
- public void Bind(string ip)
- {
- var localIpAddress = IPAddress.Parse(ip);
- try { _udpClient.Client.Close(); } catch { }
- try { _udpClient.Client.Dispose(); } catch { }
- _udpClient = new UdpClient();
- _udpClient.Client.Bind(new IPEndPoint(localIpAddress, LocalBindPort));
- }
- /// <summary>获取所有已启用、非回环/隧道网口的 IPv4 信息。</summary>
- public NetworkInterfaceInfo[] GetAllNetworkInterface()
- {
- var networks = new List<NetworkInterfaceInfo>();
- foreach (var ni in NetworkInterface.GetAllNetworkInterfaces())
- {
- if (ni.OperationalStatus != OperationalStatus.Up ||
- ni.NetworkInterfaceType == NetworkInterfaceType.Loopback ||
- ni.NetworkInterfaceType == NetworkInterfaceType.Tunnel ||
- ni.NetworkInterfaceType == NetworkInterfaceType.Unknown)
- continue;
- foreach (var ua in ni.GetIPProperties().UnicastAddresses)
- {
- if (ua.Address.AddressFamily == AddressFamily.InterNetwork)
- {
- networks.Add(new NetworkInterfaceInfo
- {
- Name = ni.Name,
- IPv4Address = ua.Address,
- IPv4Mask = ua.IPv4Mask
- });
- }
- }
- }
- return networks.ToArray();
- }
- /// <summary>在指定网卡上广播扫描并收集回应。</summary>
- public FeederNetworkInfo[] GetFeederList(NetworkInterfaceInfo network)
- {
- var feeders = new List<FeederNetworkInfo>();
- var ep = new IPEndPoint(IPAddress.Broadcast, BroadcastPort);
- var data = Encoding.UTF8.GetBytes("{udpscan}");
- try
- {
- _udpClient.Send(data, data.Length, ep);
- _udpClient.Client.ReceiveTimeout = 500;
- for (int i = 0; i < 10; i++)
- {
- try
- {
- var da = _udpClient.Receive(ref ep);
- string config = Encoding.UTF8.GetString(da, 0, da.Length).TrimEnd('\n').TrimEnd('\r');
- var info = AnalysisIP(config);
- if (info != null)
- {
- info.IsAccessible = AreFirstThreeOctetsEqual(IPAddress.Parse(info.IP), network.IPv4Address);
- info.Network = network;
- feeders.Add(info);
- }
- ep = new IPEndPoint(IPAddress.Broadcast, BroadcastPort);
- }
- catch (Exception)
- {
- break;
- }
- }
- }
- catch (Exception ex)
- {
- AppLogger.Warning($"网卡 {network.Name} 扫描异常: {ex.Message}", LogSource);
- }
- return feeders.ToArray();
- }
- /// <summary>解析 Feeder 回应串为网络信息;不匹配返回 null。</summary>
- public FeederNetworkInfo AnalysisIP(string input)
- {
- const string pattern = @"{(?<Name>[^V]+)Ver(?<Version>[^\/]+)\/ip:(?<IP>[^:]+):(?<Port>\d+)\/sub:(?<Subnet>[^\/]+)\/gw:(?<Gateway>[^\/]+)\/mac:(?<MAC>[^}]+)}";
- var match = Regex.Match(input, pattern);
- if (!match.Success) return null;
- int.TryParse(match.Groups["Port"].Value.Trim(), out var port);
- return new FeederNetworkInfo
- {
- FeederType = match.Groups["Name"].Value.Trim(),
- Version = match.Groups["Version"].Value.Trim(),
- IP = match.Groups["IP"].Value.Trim(),
- Port = port,
- Subnet = match.Groups["Subnet"].Value.Trim(),
- Gateway = match.Groups["Gateway"].Value.Trim(),
- MAC = match.Groups["MAC"].Value.Trim()
- };
- }
- /// <summary>通过 MAC 重置 Feeder 参数。</summary>
- public bool ResetFeeder(string feederMac, NetworkInterfaceInfo network = null)
- {
- try
- {
- if (network != null) Bind(network.IPv4Address.ToString());
- var ep = new IPEndPoint(IPAddress.Broadcast, BroadcastPort);
- string cmd = "{ParReset/mac:" + feederMac + "}";
- var data = Encoding.UTF8.GetBytes(cmd);
- _udpClient.Send(data, data.Length, ep);
- _udpClient.Client.ReceiveTimeout = 5000;
- var da = _udpClient.Receive(ref ep);
- string rev = Encoding.UTF8.GetString(da, 0, da.Length).TrimEnd('\n').TrimEnd('\r');
- bool ok = string.Equals(rev, cmd);
- AppLogger.Info($"重置 Feeder {feederMac} {(ok ? "成功" : "失败")}", LogSource);
- return ok;
- }
- catch (Exception ex)
- {
- AppLogger.Error($"重置 Feeder {feederMac} 异常", ex, LogSource);
- return false;
- }
- }
- /// <summary>通过 MAC 设置 Feeder 的 IP/端口/掩码。</summary>
- public async Task<bool> SetFeederIpAsync(string feederMac, string targetIp, int targetPort, string targetSub, NetworkInterfaceInfo network = null)
- {
- try
- {
- if (network != null) Bind(network.IPv4Address.ToString());
- var ep = new IPEndPoint(IPAddress.Broadcast, BroadcastPort);
- string cmd = "{ipset/mac:" + $"{feederMac}/ip:{targetIp}:{targetPort}/sub:{targetSub}" + "}";
- var data = Encoding.UTF8.GetBytes(cmd);
- await _udpClient.SendAsync(data, data.Length, ep);
- _udpClient.Client.ReceiveTimeout = 5000;
- var received = await _udpClient.ReceiveAsync();
- string rev = Encoding.UTF8.GetString(received.Buffer, 0, received.Buffer.Length).TrimEnd('\n').TrimEnd('\r');
- bool ok = string.Equals(rev, cmd);
- AppLogger.Info($"设置 Feeder {feederMac} -> {targetIp}:{targetPort}/{targetSub} {(ok ? "成功" : "失败")}", LogSource);
- return ok;
- }
- catch (Exception ex)
- {
- AppLogger.Error($"设置 Feeder {feederMac} IP 异常", ex, LogSource);
- return false;
- }
- }
- /// <summary>判断两个 IP 前三段是否相同(是否同网段)。</summary>
- public bool AreFirstThreeOctetsEqual(IPAddress ip1, IPAddress ip2)
- {
- var b1 = ip1.GetAddressBytes();
- var b2 = ip2.GetAddressBytes();
- for (int i = 0; i < 3; i++)
- if (b1[i] != b2[i]) return false;
- return true;
- }
- public void Dispose()
- {
- try { _udpClient?.Client?.Close(); } catch { }
- try { _udpClient?.Client?.Dispose(); } catch { }
- try { _udpClient?.Dispose(); } catch { }
- }
- }
- }
|