Implementation Method of LAN Service scanning in. NET,. net LAN
In a recent project, you need to meet the following requirement: In the client program, scan all the machines in the network segment where the current machine is located to see If a service is started, and list all machines that have started the service for the user to choose which service to connect. Note: The Service mentioned here is actually a program or service (such as a WCF Service) that listens to TCP-based requests on a fixed port ).
To implement this function, the core point is to initiate a TCP connection request to each IP address after obtaining the IP addresses of all machines in the same network segment of the current machine. If the request times out or other exceptions occur, if the connection is normal, the service is normal.
After implementation of basic functions and subsequent refactoring, we have the following code: an interface and a specific implementation class. It should be noted that in the following code, the interface is mentioned first, and then the specific class. during the development process, the class is created first, and then the interface is extracted. There are two reasons for extracting interfaces: one is to support IoC control inversion, and the other is to implement new functions for other similar needs in the future.
I. Interface Definition
First, let's look at the interface:
/// <Summary> /// scan service /// </summary> public interface IServerScanner {/// <summary> /// scan completed /// </summary> event EventHandler <List <ConnectionResult> OnScanComplete; /// <summary> /// report the Scan progress /// </summary> event EventHandler <ScanProgressEventArgs> OnScanProgressChanged; /// <summary> /// scan port /// </summary> int ScanPort {get; set ;} /// <summary> /// Timeout duration of a single connection /// </summary> TimeSpan Timeout {get; set ;} /// <summary> /// returns whether the specified IP address and port can be connected. /// </summary> /// <param name = "ipAddress"> </param> /// <param name = "port"> </param> /// <returns> </returns> bool IsConnected (IPAddress ipAddress, int port ); /// <summary> /// returns whether the specified IP address and port can be connected. /// </summary> /// <param name = "ip"> </param> /// <param name = "port"> </param> /// <returns> </returns> bool IsConnected (string ip, int port); // <summary> // start scanning /// </summary> void StartScan ();}
The Timeout attribute controls the Timeout duration of each connection request.
II. Specific implementation
Let's take a look at the specific implementation classes:
/// <Summary> /// scan result /// </summary> public class ConnectionResult {// <summary> /// IPAddress /// </summary> public IPAddress Address {get; set ;}//< summary> /// whether the connection is available /// </summary> public bool CanConnected {get; set ;}} /// <summary> /// scan completed event parameters /// </summary> public class ScanCompleteEventArgs {// <summary> /// result set /// </ summary> public List <ConnectionResult> Reslut {get; set; }}/// <Summary> /// Scan progress event parameters /// </summary> public class ScanProgressEventArgs {// <summary> // progress percentage /// </summary> public int Percent {get; set ;}//< summary> /// scan the services in the LAN /// </summary> public class server=: IServerScanner {// <summary> // number of IP addresses in the same network segment /// </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> /// record the number of calls/completed delegates /// </summary> private int _ totalCount = 0; public serverdetail () {Timeout = TimeSpan. fromSeconds (2) ;}/// <summary> /// this event is triggered when the scan is complete /// </summary> public event Ev EntHandler <List <ConnectionResult> OnScanComplete; // <summary> // when the Scan progress changes, trigger this event // </summary> public event EventHandler <ScanProgressEventArgs> OnScanProgressChanged; /// <summary> /// scan port /// </summary> public int ScanPort {get; set ;} /// <summary> /// Timeout duration of a single request. The default value is 2 seconds. /// </summary> public TimeSpan Timeout {get; set ;} /// <summary> /// use TcpClient to test whether the specified IP address and Port can be connected. /// </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> /// use TcpClient to test whether the specified IP address and Port can be connected. /// </summary> /// <param name = "ip"> </ param> /// <param name = "port"> </param> /// <returns> </returns> public bool IsConnec Ted (string ip, int port) {IPAddress ipAddress; if (IPAddress. tryParse (ip, out ipAddress) {return IsConnected (ipAddress, port);} else {throw new ArgumentException ("Incorrect IP address format ");}} /// <summary> /// start to scan the current network segment /// </summary> public void StartScan () {if (ScanPort = 0) {throw new InvalidOperationException ("Scan port ScanPort must be specified");} // clear possible data _ resultList. clear (); _ totalCount = 0; _ startTime = DateTimeOffset. now; // obtain the IP var ipList = GetAllRemoteIPList () in this section; // generate the delegate 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) ;}// asynchronously call each delegate for (int I = 0; I <SegmentIpMaxCount; I ++) {funcs [I]. beginInvoke (ip List [I], ScanPort, OnComplete, funcs [I]); _ totalCount + = 1 ;}} /// <summary> /// obtain all IP addresses in this segment /// </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 ("the current computer does not have an intranet IP Address");} var localInterIPBytes = localInterIP. getAddressBytes (); for (int I = 1; I <= SegmentIpMaxCount; I ++) {// replace 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) {// Add to result _ resultList. add (result); // report progress _ 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) {// result if (SynchronizationContext through the event throw. current ==_ originalContext) {OnScanComplete ?. Invoke (this, _ resultList);} else {_ originalContext. Post (conState => {OnScanComplete ?. Invoke (this, _ resultList) ;}, null) ;}// Debug the computing duration. writeLine ("Compete"); _ endTime = DateTimeOffset. now; Debug. writeLine ($ "Duration: {_ endTime-_ startTime }");}}} /// <summary> /// test whether the connection can be established. /// </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 ;}} server=
The comments in the above Code are basically more detailed. Here are a few simple points:
The TestConnection function implements the current core function, that is, to request the given IP address and port, and return the result. Specifically, it controls timeout by calling the WaitOne method of the IAsyncResult. AsyncWaitHandle attribute;
In the StartScan method, after obtaining the IP list, the entire method is implemented asynchronously by generating the delegation list and asynchronously calling these delegates, without blocking the UI, the delegate points to the TestConnection function;
The synchronous context SynchronizationContext ensures that the caller processes progress update events or scan completion events on the original thread (usually the UI thread;
After the asynchronous completion of each delegate, the callback method OnComplete is executed. In this method, the global variable operation needs to be locked to ensure thread security.
Iii. How to Use
Finally, let's take a look at how to use it, which is very simple:
Private void View_Loaded () {// Add the following code to the Load event on the page: servermetadata. onScanComplete + = ServerScanner_OnScanComplete; servercomplete. onScanProgressChanged + = ServerScanner_OnScanProgressChanged; // scan the port number serverslave. scanPort = 7890;} private void StartScan () {// start scanning serverdetail. startScan ();} private void ServerScanner_OnScanComplete (object sender, List <ConnectionResult> e ){...} private void ServerScanner_OnScanProgressChanged (object sender, ScanProgressEventArgs e ){...}
If you have better suggestions or comments, please leave a message to communicate with each other.
In. NET, the implementation of the LAN scanning service is all the content that I have shared with you. I hope to give you a reference and support for the customer's house.