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 { }
}
}
}