Skip to content

Commit 40031f2

Browse files
committed
[增加] 网络工具类函数扩展
1 parent c06fc6b commit 40031f2

1 file changed

Lines changed: 223 additions & 0 deletions

File tree

GameFrameX.Utility/NetHelper.cs

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,4 +179,227 @@ public static List<string> GetLocalIpList(AddressFamily addressFamily = AddressF
179179

180180
return ipList;
181181
}
182+
183+
/// <summary>
184+
/// 检测网络连通性
185+
/// </summary>
186+
/// <param name="host">目标主机地址</param>
187+
/// <param name="timeout">超时时间(毫秒),默认5000毫秒</param>
188+
/// <returns>如果网络连通,返回true;否则返回false</returns>
189+
/// <exception cref="ArgumentException">当host为null或空字符串时抛出此异常</exception>
190+
/// <exception cref="ArgumentOutOfRangeException">当timeout小于0时抛出此异常</exception>
191+
public static bool IsNetworkReachable(string host, int timeout = 5000)
192+
{
193+
ArgumentException.ThrowIfNullOrEmpty(host, nameof(host));
194+
ArgumentOutOfRangeException.ThrowIfNegative(timeout, nameof(timeout));
195+
196+
try
197+
{
198+
using var ping = new Ping();
199+
var reply = ping.Send(host, timeout);
200+
return reply.Status == IPStatus.Success;
201+
}
202+
catch
203+
{
204+
return false;
205+
}
206+
}
207+
208+
/// <summary>
209+
/// 获取本机MAC地址列表
210+
/// </summary>
211+
/// <returns>MAC地址字符串列表</returns>
212+
public static List<string> GetMacAddresses()
213+
{
214+
var macAddresses = new List<string>();
215+
try
216+
{
217+
var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
218+
foreach (var network in networkInterfaces)
219+
{
220+
// 排除环回接口和非活动接口
221+
if (network.NetworkInterfaceType == NetworkInterfaceType.Loopback ||
222+
network.OperationalStatus != OperationalStatus.Up)
223+
{
224+
continue;
225+
}
226+
227+
var macAddress = network.GetPhysicalAddress().ToString();
228+
if (!string.IsNullOrEmpty(macAddress))
229+
{
230+
// 格式化MAC地址为标准格式 (XX:XX:XX:XX:XX:XX)
231+
var formattedMac = string.Join(":",
232+
Enumerable.Range(0, macAddress.Length / 2)
233+
.Select(i => macAddress.Substring(i * 2, 2)));
234+
macAddresses.Add(formattedMac);
235+
}
236+
}
237+
}
238+
catch
239+
{
240+
// 发生异常时返回空列表
241+
}
242+
243+
return macAddresses;
244+
}
245+
246+
/// <summary>
247+
/// 验证IP地址是否在指定的子网内
248+
/// </summary>
249+
/// <param name="ipAddress">要验证的IP地址</param>
250+
/// <param name="networkAddress">网络地址</param>
251+
/// <param name="subnetMask">子网掩码</param>
252+
/// <returns>如果IP地址在子网内,返回true;否则返回false</returns>
253+
/// <exception cref="ArgumentException">当IP地址格式无效时抛出此异常</exception>
254+
public static bool IsIpInSubnet(string ipAddress, string networkAddress, string subnetMask)
255+
{
256+
ArgumentException.ThrowIfNullOrEmpty(ipAddress, nameof(ipAddress));
257+
ArgumentException.ThrowIfNullOrEmpty(networkAddress, nameof(networkAddress));
258+
ArgumentException.ThrowIfNullOrEmpty(subnetMask, nameof(subnetMask));
259+
260+
if (!IPAddress.TryParse(ipAddress, out var ip) ||
261+
!IPAddress.TryParse(networkAddress, out var network) ||
262+
!IPAddress.TryParse(subnetMask, out var mask))
263+
{
264+
throw new ArgumentException("无效的IP地址格式");
265+
}
266+
267+
var ipBytes = ip.GetAddressBytes();
268+
var networkBytes = network.GetAddressBytes();
269+
var maskBytes = mask.GetAddressBytes();
270+
271+
if (ipBytes.Length != networkBytes.Length || ipBytes.Length != maskBytes.Length)
272+
{
273+
return false;
274+
}
275+
276+
for (int i = 0; i < ipBytes.Length; i++)
277+
{
278+
if ((ipBytes[i] & maskBytes[i]) != (networkBytes[i] & maskBytes[i]))
279+
{
280+
return false;
281+
}
282+
}
283+
284+
return true;
285+
}
286+
287+
/// <summary>
288+
/// 获取指定范围内的可用端口列表
289+
/// </summary>
290+
/// <param name="startPort">起始端口号</param>
291+
/// <param name="endPort">结束端口号</param>
292+
/// <param name="maxCount">最大返回数量,默认为10</param>
293+
/// <returns>可用端口号列表</returns>
294+
/// <exception cref="ArgumentOutOfRangeException">当端口范围无效时抛出此异常</exception>
295+
public static List<int> GetAvailablePorts(int startPort, int endPort, int maxCount = 10)
296+
{
297+
ArgumentOutOfRangeException.ThrowIfLessThan(startPort, 1, nameof(startPort));
298+
ArgumentOutOfRangeException.ThrowIfGreaterThan(startPort, 65535, nameof(startPort));
299+
ArgumentOutOfRangeException.ThrowIfLessThan(endPort, 1, nameof(endPort));
300+
ArgumentOutOfRangeException.ThrowIfGreaterThan(endPort, 65535, nameof(endPort));
301+
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(startPort, endPort, nameof(startPort));
302+
ArgumentOutOfRangeException.ThrowIfLessThan(maxCount, 1, nameof(maxCount));
303+
304+
var availablePorts = new List<int>();
305+
var usedPorts = PortIsUsed().ToHashSet();
306+
307+
for (int port = startPort; port < endPort && availablePorts.Count < maxCount; port++)
308+
{
309+
if (!usedPorts.Contains(port))
310+
{
311+
availablePorts.Add(port);
312+
}
313+
}
314+
315+
return availablePorts;
316+
}
317+
318+
/// <summary>
319+
/// 获取本机的公网IP地址
320+
/// </summary>
321+
/// <param name="timeout">超时时间(毫秒),默认10000毫秒</param>
322+
/// <returns>公网IP地址字符串,获取失败返回null</returns>
323+
/// <exception cref="ArgumentOutOfRangeException">当timeout小于0时抛出此异常</exception>
324+
public static async Task<string?> GetPublicIpAddressAsync(int timeout = 10000)
325+
{
326+
ArgumentOutOfRangeException.ThrowIfNegative(timeout, nameof(timeout));
327+
328+
var services = new[]
329+
{
330+
"https://api.ipify.org",
331+
"https://icanhazip.com",
332+
"https://ipinfo.io/ip"
333+
};
334+
335+
using var httpClient = new HttpClient { Timeout = TimeSpan.FromMilliseconds(timeout) };
336+
337+
foreach (var service in services)
338+
{
339+
try
340+
{
341+
var response = await httpClient.GetStringAsync(service);
342+
var ip = response.Trim();
343+
if (IsValidIpAddress(ip, out _))
344+
{
345+
return ip;
346+
}
347+
}
348+
catch
349+
{
350+
// 尝试下一个服务
351+
continue;
352+
}
353+
}
354+
355+
return null;
356+
}
357+
358+
/// <summary>
359+
/// 判断IP地址是否为私有地址
360+
/// </summary>
361+
/// <param name="ipAddress">IP地址字符串</param>
362+
/// <returns>如果是私有地址,返回true;否则返回false</returns>
363+
/// <exception cref="ArgumentException">当IP地址格式无效时抛出此异常</exception>
364+
public static bool IsPrivateIpAddress(string ipAddress)
365+
{
366+
ArgumentException.ThrowIfNullOrEmpty(ipAddress, nameof(ipAddress));
367+
368+
if (!IPAddress.TryParse(ipAddress, out var ip))
369+
{
370+
throw new ArgumentException("无效的IP地址格式", nameof(ipAddress));
371+
}
372+
373+
var bytes = ip.GetAddressBytes();
374+
375+
// IPv4私有地址范围
376+
if (ip.AddressFamily == AddressFamily.InterNetwork)
377+
{
378+
// 10.0.0.0/8
379+
if (bytes[0] == 10)
380+
{
381+
return true;
382+
}
383+
384+
// 172.16.0.0/12
385+
if (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31)
386+
{
387+
return true;
388+
}
389+
390+
// 192.168.0.0/16
391+
if (bytes[0] == 192 && bytes[1] == 168)
392+
{
393+
return true;
394+
}
395+
396+
// 127.0.0.0/8 (环回地址)
397+
if (bytes[0] == 127)
398+
{
399+
return true;
400+
}
401+
}
402+
403+
return false;
404+
}
182405
}

0 commit comments

Comments
 (0)