• <fieldset id="8imwq"><menu id="8imwq"></menu></fieldset>
  • <bdo id="8imwq"><input id="8imwq"></input></bdo>
    最新文章專題視頻專題問答1問答10問答100問答1000問答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
    問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
    當(dāng)前位置: 首頁 - 科技 - 知識百科 - 正文

    在.NET中掃描局域網(wǎng)服務(wù)的實(shí)現(xiàn)方法

    來源:懂視網(wǎng) 責(zé)編:小采 時間:2020-11-27 22:35:10
    文檔

    在.NET中掃描局域網(wǎng)服務(wù)的實(shí)現(xiàn)方法

    在.NET中掃描局域網(wǎng)服務(wù)的實(shí)現(xiàn)方法:在最近負(fù)責(zé)的項(xiàng)目中,需要實(shí)現(xiàn)這樣一個需求:在客戶端程序中,掃描當(dāng)前機(jī)器所在網(wǎng)段中的所有機(jī)器上是否有某服務(wù)啟動,并把所有已經(jīng)啟動服務(wù)的機(jī)器列出來,供用戶選擇,連接哪個服務(wù)。注意:這里所說的服務(wù)事實(shí)上就是在一個固定的端口監(jiān)聽基于 TCP 協(xié)議的請求
    推薦度:
    導(dǎo)讀在.NET中掃描局域網(wǎng)服務(wù)的實(shí)現(xiàn)方法:在最近負(fù)責(zé)的項(xiàng)目中,需要實(shí)現(xiàn)這樣一個需求:在客戶端程序中,掃描當(dāng)前機(jī)器所在網(wǎng)段中的所有機(jī)器上是否有某服務(wù)啟動,并把所有已經(jīng)啟動服務(wù)的機(jī)器列出來,供用戶選擇,連接哪個服務(wù)。注意:這里所說的服務(wù)事實(shí)上就是在一個固定的端口監(jiān)聽基于 TCP 協(xié)議的請求

    在最近負(fù)責(zé)的項(xiàng)目中,需要實(shí)現(xiàn)這樣一個需求:在客戶端程序中,掃描當(dāng)前機(jī)器所在網(wǎng)段中的所有機(jī)器上是否有某服務(wù)啟動,并把所有已經(jīng)啟動服務(wù)的機(jī)器列出來,供用戶選擇,連接哪個服務(wù)。注意:這里所說的服務(wù)事實(shí)上就是在一個固定的端口監(jiān)聽基于 TCP 協(xié)議的請求的程序或者服務(wù)(如 WCF 服務(wù))。

    要實(shí)現(xiàn)這樣的功能,核心的一點(diǎn)就是在得到當(dāng)前機(jī)器同網(wǎng)段的所有機(jī)器的 IP 后,對每一 IP 發(fā)生 TCP 連接請求,如果請求超時或者出現(xiàn)其它異常,則認(rèn)為沒有服務(wù),反之,如果能夠正常連接,則認(rèn)為服務(wù)正常。

    經(jīng)過基本功能的實(shí)現(xiàn)以及后續(xù)的重構(gòu)之后,就有了本文以下的代碼:一個接口和具體實(shí)現(xiàn)的類。需要說明的是:在下面的代碼中,先提到接口,再提到具體類;而在開發(fā)過程中,則是首先創(chuàng)建了類,然后才提取了接口。之所以要提取接口,原因有二:一是可以支持 IoC控制反轉(zhuǎn);二是將來如果其它的同類需求,可以其于此接口實(shí)現(xiàn)新功能。

    一、接口定義

    先看來一下接口:

    /// <summary>
     /// 掃描服務(wù)
     /// </summary>
     public interface IServerScanner
     {
     /// <summary>
     /// 掃描完成
     /// </summary>
     event EventHandler<List<ConnectionResult>> OnScanComplete;
     /// <summary>
     /// 報告掃描進(jìn)度
     /// </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 屬性是控制每次連接請求超時的時長。

    二、具體實(shí)現(xiàn)

    再來看一下具體實(shí)現(xiàn)類:

    /// <summary>
     /// 掃描結(jié)果
     /// </summary>
     public class ConnectionResult
     {
     /// <summary>
     /// IPAddress 地址
     /// </summary>
     public IPAddress Address { get; set; }
     /// <summary>
     /// 是否可連接上
     /// </summary>
     public bool CanConnected { get; set; }
     }
     /// <summary>
     /// 掃描完成事件參數(shù)
     /// </summary>
     public class ScanCompleteEventArgs
     {
     /// <summary>
     /// 結(jié)果集合
     /// </summary>
     public List<ConnectionResult> Reslut { get; set; }
     }
     /// <summary>
     /// 掃描進(jìn)度事件參數(shù)
     /// </summary>
     public class ScanProgressEventArgs
     {
     /// <summary>
     /// 進(jìn)度百分比
     /// </summary>
     public int Percent { get; set; }
     }
     /// <summary>
     /// 掃描局域網(wǎng)中的服務(wù)
     /// </summary>
     public class ServerScanner : IServerScanner
     {
     /// <summary>
     /// 同一網(wǎng)段內(nèi) IP 地址的數(shù)量
     /// </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>
     /// 記錄調(diào)用/完成委托的數(shù)量
     /// </summary>
     private int _totalCount = 0;
     public ServerScanner()
     {
     Timeout = TimeSpan.FromSeconds(2);
     }
     /// <summary>
     /// 當(dāng)掃描完成時,觸發(fā)此事件
     /// </summary>
     public event EventHandler<List<ConnectionResult>> OnScanComplete;
     /// <summary>
     /// 當(dāng)掃描進(jìn)度發(fā)生更改時,觸發(fā)此事件
     /// </summary>
     public event EventHandler<ScanProgressEventArgs> OnScanProgressChanged;
     /// <summary>
     /// 掃描端口
     /// </summary>
     public int ScanPort { get; set; }
     /// <summary>
     /// 單次請求的超時時長,默認(rèn)為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>
     /// 開始掃描當(dāng)前網(wǎng)段
     /// </summary>
     public void StartScan()
     {
     if (ScanPort == 0)
     {
     throw new InvalidOperationException("必須指定掃描的端口 ScanPort");
     }
     // 清除可能存在的數(shù)據(jù)
     _resultList.Clear();
     _totalCount = 0;
     _startTime = DateTimeOffset.Now;
     // 得到本網(wǎng)段的 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);
     }
     // 異步調(diào)用每個委托
     for (int i = 0; i < SegmentIpMaxCount; i++)
     {
     funcs[i].BeginInvoke(ipList[i], ScanPort, OnComplete, funcs[i]);
     _totalCount += 1;
     }
     }
     /// <summary>
     /// 得到本網(wǎng)段的所有 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("當(dāng)前計算機(jī)不存在內(nèi)網(wǎng) IP");
     }
     var localInterIPBytes = localInterIP.GetAddressBytes();
     for (int i = 1; i <= SegmentIpMaxCount; i++)
     {
     // 對末位進(jìn)行替換
     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)
     {
     // 添加到結(jié)果中
     _resultList.Add(result);
     // 報告進(jìn)度
     _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)
     {
     // 通過事件拋出結(jié)果
     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

    以上代碼中注釋基本上已經(jīng)比較詳細(xì),這里再簡單提幾個點(diǎn):

    TestConnection 函數(shù)實(shí)了現(xiàn)核心功能,即請求給定的 IP 和端口,并返回結(jié)果;其中通過調(diào)用 IAsyncResult.AsyncWaitHandle 屬性的 WaitOne 方法來實(shí)現(xiàn)對超時的控制;

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

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

    對于每個委托異步完成后,會執(zhí)行回調(diào)方法 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中掃描局域網(wǎng)服務(wù)的實(shí)現(xiàn)方法就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。

    聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    在.NET中掃描局域網(wǎng)服務(wù)的實(shí)現(xiàn)方法

    在.NET中掃描局域網(wǎng)服務(wù)的實(shí)現(xiàn)方法:在最近負(fù)責(zé)的項(xiàng)目中,需要實(shí)現(xiàn)這樣一個需求:在客戶端程序中,掃描當(dāng)前機(jī)器所在網(wǎng)段中的所有機(jī)器上是否有某服務(wù)啟動,并把所有已經(jīng)啟動服務(wù)的機(jī)器列出來,供用戶選擇,連接哪個服務(wù)。注意:這里所說的服務(wù)事實(shí)上就是在一個固定的端口監(jiān)聽基于 TCP 協(xié)議的請求
    推薦度:
    標(biāo)簽: 中的 方法 掃描
    • 熱門焦點(diǎn)

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    主站蜘蛛池模板: 久久精品亚洲日本波多野结衣| 亚洲国产精品人人做人人爽| 97精品国产97久久久久久免费| 2021国产精品成人免费视频| 国自产精品手机在线观看视| 国产精品无码日韩欧| 免费精品视频在线| 97国产视频精品| 国产精品亚洲欧美一区麻豆| 四虎国产精品永久在线| 久久精品国产亚洲一区二区三区| 亚洲精品专区| 国产亚洲午夜高清国产拍精品| 久久人人爽人人精品视频| 国产精品久久网| 国语自产少妇精品视频蜜桃| 久热精品视频第一页| 国产精品国产三级国产a| 日韩欧美精品不卡| 国产91精品在线观看| 久久免费的精品国产V∧| 亚洲国产精品自产在线播放| 久久97久久97精品免视看秋霞| 国内精品久久久久久99| 亚洲精品欧美精品日韩精品| 九九精品在线视频| 国产精品亚洲高清一区二区| 99视频在线精品国自产拍亚瑟| 亚洲精品国产首次亮相| 午夜一级日韩精品制服诱惑我们这边| 国产色婷婷五月精品综合在线| 国产精品无码一区二区在线观一| 亚洲国产一成人久久精品| 免费精品久久久久久中文字幕 | 国产AV国片精品一区二区| 精品国产第一国产综合精品 | 久久免费的精品国产V∧| 亚洲一区爱区精品无码 | 国产亚洲精品精品国产亚洲综合| 亚洲国产精品一区二区第一页| 亚洲精品高清久久|