@@ -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