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 { /// /// 网卡接口信息(IPv4)。 /// public class NetworkInterfaceInfo { public string Name { get; set; } public IPAddress IPv4Address { get; set; } public IPAddress IPv4Mask { get; set; } } /// /// UDP 扫描发现的 Feeder 网络信息。 /// 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; /// 是否与本机某网卡处于同一网段(前三段相同)。 public bool IsAccessible { get { return _isAccessible; } set { SetProperty(ref _isAccessible, value); } } /// 发现该 Feeder 所使用的本机网卡。 public NetworkInterfaceInfo Network { get; set; } public override string ToString() => $"{FeederType} {IP}:{Port} ({MAC})"; public event PropertyChangedEventHandler PropertyChanged; private void SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null) { if (EqualityComparer.Default.Equals(storage, value)) return; storage = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } /// /// 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:...}。 /// public class FeederIpScanner { private const int BroadcastPort = 1234; private const int LocalBindPort = 8000; private const string LogSource = "Feeder.Scan"; private UdpClient _udpClient = new UdpClient(); /// 扫描所有网卡,返回发现的 Feeder 列表。 public async Task> ScanAsync() { return await Task.Run(() => { var result = new List(); 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; }); } /// 绑定到指定本机网卡 IP 与固定本地端口。 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)); } /// 获取所有已启用、非回环/隧道网口的 IPv4 信息。 public NetworkInterfaceInfo[] GetAllNetworkInterface() { var networks = new List(); 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(); } /// 在指定网卡上广播扫描并收集回应。 public FeederNetworkInfo[] GetFeederList(NetworkInterfaceInfo network) { var feeders = new List(); 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(); } /// 解析 Feeder 回应串为网络信息;不匹配返回 null。 public FeederNetworkInfo AnalysisIP(string input) { const string pattern = @"{(?[^V]+)Ver(?[^\/]+)\/ip:(?[^:]+):(?\d+)\/sub:(?[^\/]+)\/gw:(?[^\/]+)\/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() }; } /// 通过 MAC 重置 Feeder 参数。 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; } } /// 通过 MAC 设置 Feeder 的 IP/端口/掩码。 public async Task 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; } } /// 判断两个 IP 前三段是否相同(是否同网段)。 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 { } } } }