首页 > 编程 > .NET > 正文

在.NET中扫描局域网服务的实现方法

2024-07-10 12:48:50
字体:
来源:转载
供稿:网友

在最近负责的项目中,需要实现这样一个需求:在客户端程序中,扫描当前机器所在网段中的所有机器上是否有某服务启动,并把所有已经启动服务的机器列出来,供用户选择,连接哪个服务。注意:这里所说的服务事实上就是在一个固定的端口监听基于 TCP 协议的请求的程序或者服务(如 WCF 服务)。

要实现这样的功能,核心的一点就是在得到当前机器同网段的所有机器的 IP 后,对每一 IP 发生 TCP 连接请求,如果请求超时或者出现其它异常,则认为没有服务,反之,如果能够正常连接,则认为服务正常。

经过基本功能的实现以及后续的重构之后,就有了本文以下的代码:一个接口和具体实现的类。需要说明的是:在下面的代码中,先提到接口,再提到具体类;而在开发过程中,则是首先创建了类,然后才提取了接口。之所以要提取接口,原因有二:一是可以支持 IoC控制反转;二是将来如果其它的同类需求,可以其于此接口实现新功能。

一、接口定义

先看来一下接口:

/// <summary> /// 扫描服务 /// </summary> public interface IServerScanner { /// <summary> /// 扫描完成 /// </summary> event EventHandler<List<ConnectionResult>> OnScanComplete; /// <summary> /// 报告扫描进度 /// </summary> event EventHandler<ScanProgressEventArgs> OnScanProgressChanged; /// <summary> /// 扫描端口 /// </summary> int ScanPort { get; set; } /// <summary> /// 单次连接超时时长 /// </summary> TimeSpan Timeout { get; set; } /// <summary> /// 返回指定的IP与端口是否能够连接上 /// </summary> /// <param name="ipAddress"></param> /// <param name="port"></param> /// <returns></returns> bool IsConnected(IPAddress ipAddress, int port); /// <summary> /// 返回指定的IP与端口是否能够连接上 /// </summary> /// <param name="ip"></param> /// <param name="port"></param> /// <returns></returns> bool IsConnected(string ip, int port); /// <summary> /// 开始扫描 /// </summary> void StartScan(); }

其中 Timeout 属性是控制每次连接请求超时的时长。

二、具体实现

再来看一下具体实现类:

/// <summary> /// 扫描结果 /// </summary> public class ConnectionResult { /// <summary> /// IPAddress 地址 /// </summary> public IPAddress Address { get; set; } /// <summary> /// 是否可连接上 /// </summary> public bool CanConnected { get; set; } } /// <summary> /// 扫描完成事件参数 /// </summary> public class ScanCompleteEventArgs { /// <summary> /// 结果集合 /// </summary> public List<ConnectionResult> Reslut { get; set; } } /// <summary> /// 扫描进度事件参数 /// </summary> public class ScanProgressEventArgs { /// <summary> /// 进度百分比 /// </summary> public int Percent { get; set; } } /// <summary> /// 扫描局域网中的服务 /// </summary> public class ServerScanner : IServerScanner { /// <summary> /// 同一网段内 IP 地址的数量 /// </summary> private const int SegmentIpMaxCount = 255; private DateTimeOffset _endTime; private object _locker = new object(); private SynchronizationContext _originalContext = SynchronizationContext.Current; private List<ConnectionResult> _resultList = new List<ConnectionResult>(); private DateTimeOffset _startTime; /// <summary> /// 记录调用/完成委托的数量 /// </summary> private int _totalCount = 0; public ServerScanner() {  Timeout = TimeSpan.FromSeconds(2); } /// <summary> /// 当扫描完成时,触发此事件 /// </summary> public event EventHandler<List<ConnectionResult>> OnScanComplete; /// <summary> /// 当扫描进度发生更改时,触发此事件 /// </summary> public event EventHandler<ScanProgressEventArgs> OnScanProgressChanged; /// <summary> /// 扫描端口 /// </summary> public int ScanPort { get; set; } /// <summary> /// 单次请求的超时时长,默认为2秒 /// </summary> public TimeSpan Timeout { get; set; } /// <summary> /// 使用 TcpClient 测试是否可以连上指定的 IP 与 Port /// </summary> /// <param name="ipAddress"></param> /// <param name="port"></param> /// <returns></returns> public bool IsConnected(IPAddress ipAddress, int port) {  var result = TestConnection(ipAddress, port);  return result.CanConnected; } /// <summary> /// 使用 TcpClient 测试是否可以连上指定的 IP 与 Port /// </summary> /// <param name="ip"></param> /// <param name="port"></param> /// <returns></returns> public bool IsConnected(string ip, int port) {  IPAddress ipAddress;  if (IPAddress.TryParse(ip, out ipAddress))  {  return IsConnected(ipAddress, port);  }  else  {  throw new ArgumentException("IP 地址格式不正确");  } } /// <summary> /// 开始扫描当前网段 /// </summary> public void StartScan() {  if (ScanPort == 0)  {  throw new InvalidOperationException("必须指定扫描的端口 ScanPort");  }  // 清除可能存在的数据  _resultList.Clear();  _totalCount = 0;  _startTime = DateTimeOffset.Now;  // 得到本网段的 IP  var ipList = GetAllRemoteIPList();  // 生成委托列表  List<Func<IPAddress, int, ConnectionResult>> funcs = new List<Func<IPAddress, int, ConnectionResult>>();  for (int i = 0; i < SegmentIpMaxCount; i++)  {  var tmpF = new Func<IPAddress, int, ConnectionResult>(TestConnection);  funcs.Add(tmpF);  }  // 异步调用每个委托  for (int i = 0; i < SegmentIpMaxCount; i++)  {  funcs[i].BeginInvoke(ipList[i], ScanPort, OnComplete, funcs[i]);  _totalCount += 1;  } } /// <summary> /// 得到本网段的所有 IP /// </summary> /// <returns></returns> private List<IPAddress> GetAllRemoteIPList() {  var localName = Dns.GetHostName();  var localIPEntry = Dns.GetHostEntry(localName);  List<IPAddress> ipList = new List<IPAddress>();  IPAddress localInterIP = localIPEntry.AddressList.FirstOrDefault(m => m.AddressFamily == AddressFamily.InterNetwork);  if (localInterIP == null)  {  throw new InvalidOperationException("当前计算机不存在内网 IP");  }  var localInterIPBytes = localInterIP.GetAddressBytes();  for (int i = 1; i <= SegmentIpMaxCount; i++)  {  // 对末位进行替换  localInterIPBytes[3] = (byte)i;  ipList.Add(new IPAddress(localInterIPBytes));  }  return ipList; } private void OnComplete(IAsyncResult ar) {  var state = ar.AsyncState as Func<IPAddress, int, ConnectionResult>;  var result = state.EndInvoke(ar);  lock (_locker)  {  // 添加到结果中  _resultList.Add(result);  // 报告进度  _totalCount -= 1;  var percent = (SegmentIpMaxCount - _totalCount) * 100 / SegmentIpMaxCount;  if (SynchronizationContext.Current == _originalContext)  {   OnScanProgressChanged?.Invoke(this, new ScanProgressEventArgs { Percent = percent });  }  else  {   _originalContext.Post(conState =>   {   OnScanProgressChanged?.Invoke(this, new ScanProgressEventArgs { Percent = percent });   }, null);  }  if (_totalCount == 0)  {   // 通过事件抛出结果   if (SynchronizationContext.Current == _originalContext)   {   OnScanComplete?.Invoke(this, _resultList);   }   else   {   _originalContext.Post(conState =>   {    OnScanComplete?.Invoke(this, _resultList);   }, null);   }   // 计算耗时   Debug.WriteLine("Compete");   _endTime = DateTimeOffset.Now;   Debug.WriteLine($"Duration: {_endTime - _startTime}");  }  } } /// <summary> /// 测试是否可以连接到 /// </summary> /// <param name="address"></param> /// <param name="port"></param> /// <returns></returns> private ConnectionResult TestConnection(IPAddress address, int port) {  TcpClient c = new TcpClient();  ConnectionResult result = new ConnectionResult();  result.Address = address;  using (TcpClient tcp = new TcpClient())  {  IAsyncResult ar = tcp.BeginConnect(address, port, null, null);  WaitHandle wh = ar.AsyncWaitHandle;  try  {   if (!ar.AsyncWaitHandle.WaitOne(Timeout, false))   {   tcp.Close();   }   else   {   tcp.EndConnect(ar);   result.CanConnected = true;   }  }  catch  {  }  finally  {   wh.Close();  }  }  return result; } }ServerScanner            
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表