中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

.NET中如何實現掃描局域網服務

發布時間:2021-06-17 15:48:57 來源:億速云 閱讀:170 作者:小新 欄目:開發技術

小編給大家分享一下.NET中如何實現掃描局域網服務,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

在最近負責的項目中,需要實現這樣一個需求:在客戶端程序中,掃描當前機器所在網段中的所有機器上是否有某服務啟動,并把所有已經啟動服務的機器列出來,供用戶選擇,連接哪個服務。注意:這里所說的服務事實上就是在一個固定的端口監聽基于 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

以上代碼中注釋基本上已經比較詳細,這里再簡單提幾個點:

TestConnection 函數實了現核心功能,即請求給定的 IP 和端口,并返回結果;其中通過調用 IAsyncResult.AsyncWaitHandle 屬性的 WaitOne 方法來實現對超時的控制;

StartScan 方法中,在得到 IP 列表后,通過生成委托列表并異步調用這些委托來實現整個方法是異步的,不會阻塞 UI,而這些委托指向的方法就是 TestConnection 函數;

使用同步上下文 SynchronizationContext,可以保證調用方在原來的線程(通常是 UI 線程)上處理進度更新事件或掃描完成事件;

對于每個委托異步完成后,會執行回調方法 OnComplete,在它里面,對全局變量的操作需要加鎖,以保證線程安全。

三、如何使用

最后來看一下如何使用,非常簡單:

private void View_Loaded()
  {
   // 在界面 Load 事件中添加以下代碼
   ServerScanner.OnScanComplete += ServerScanner_OnScanComplete;
   ServerScanner.OnScanProgressChanged += ServerScanner_OnScanProgressChanged;
   // 掃描的端口號
   ServerScanner.ScanPort = 7890;
  }
  private void StartScan()
  {
   // 開始掃描
   ServerScanner.StartScan();
  }

  private void ServerScanner_OnScanComplete(object sender, List<ConnectionResult> e)
  {
   ...
  }
  private void ServerScanner_OnScanProgressChanged(object sender, ScanProgressEventArgs e)
  {
   ...
  }

以上是“.NET中如何實現掃描局域網服務”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

福清市| 堆龙德庆县| 荥阳市| 保靖县| 涿州市| 瓦房店市| 西吉县| 东乡族自治县| 根河市| 伽师县| 崇阳县| 深泽县| 晋宁县| 安吉县| 沾化县| 岳普湖县| 肇东市| 沙洋县| 西充县| 台北县| 时尚| 鄂温| 河北省| 乡宁县| 石嘴山市| 曲靖市| 菏泽市| 宁都县| 桃江县| 濮阳市| 江门市| 民权县| 丹江口市| 通许县| 慈溪市| 汾阳市| 巫溪县| 麟游县| 临猗县| 杂多县| 泰兴市|