Skip to content

Commit 249b2ae

Browse files
committed
* Added multithreaded port scanning
* Fixed all UDP ports being displayed as OPEN * Connection timeout can now be set in millliseconds, seconds and minutes * Design changes * Improved some code documentation
1 parent fc993c9 commit 249b2ae

15 files changed

Lines changed: 296 additions & 140 deletions

Advanced PortChecker/Advanced PortChecker.csproj

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,11 @@
7171
</ApplicationDefinition>
7272
<Compile Include="Classes\Export\ExportType.cs" />
7373
<Compile Include="Classes\Export\ExportWriter.cs" />
74-
<Compile Include="Classes\Controls\LvCheck.cs" />
75-
<Compile Include="Classes\Scanner\OperationInformation.cs" />
76-
<Compile Include="Classes\Scanner\PortChecker.cs" />
77-
<Compile Include="Classes\StyleManager.cs" />
74+
<Compile Include="Classes\Objects\LvCheck.cs" />
75+
<Compile Include="Classes\Scanner\ScanOperation.cs" />
76+
<Compile Include="Classes\Scanner\PortScanner.cs" />
77+
<Compile Include="Classes\GUI\StyleManager.cs" />
78+
<Compile Include="Classes\Utils\ThreadCalculator.cs" />
7879
<Compile Include="Windows\AboutWindow.xaml.cs">
7980
<DependentUpon>AboutWindow.xaml</DependentUpon>
8081
</Compile>

Advanced PortChecker/App.config

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<value>True</value>
2424
</setting>
2525
<setting name="TimeOut" serializeAs="String">
26-
<value>1000</value>
26+
<value>500</value>
2727
</setting>
2828
<setting name="WindowOpacity" serializeAs="String">
2929
<value>1</value>
@@ -35,7 +35,10 @@
3535
<value>True</value>
3636
</setting>
3737
<setting name="TimeOutType" serializeAs="String">
38-
<value>1</value>
38+
<value>0</value>
39+
</setting>
40+
<setting name="ScanThreads" serializeAs="String">
41+
<value>2</value>
3942
</setting>
4043
</Advanced_PortChecker.Properties.Settings>
4144
</userSettings>

Advanced PortChecker/Classes/Export/ExportWriter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System.IO;
33
using System.Text;
44
using System.Windows.Controls;
5-
using Advanced_PortChecker.Classes.Controls;
5+
using Advanced_PortChecker.Classes.Objects;
66

77
namespace Advanced_PortChecker.Classes.Export
88
{

Advanced PortChecker/Classes/StyleManager.cs renamed to Advanced PortChecker/Classes/GUI/StyleManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.Windows.Media;
44
using Syncfusion.Windows.Shared;
55

6-
namespace Advanced_PortChecker.Classes
6+
namespace Advanced_PortChecker.Classes.GUI
77
{
88
/// <summary>
99
/// Static class to change the style of an object

Advanced PortChecker/Classes/Controls/LvCheck.cs renamed to Advanced PortChecker/Classes/Objects/LvCheck.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Advanced_PortChecker.Classes.Controls
1+
namespace Advanced_PortChecker.Classes.Objects
22
{
33
/// <summary>
44
/// Represents the content of a ListViewItem

Advanced PortChecker/Classes/Scanner/PortChecker.cs renamed to Advanced PortChecker/Classes/Scanner/PortScanner.cs

Lines changed: 80 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
using System.Collections.Generic;
33
using System.Net;
44
using System.Net.Sockets;
5-
using System.Threading.Tasks;
6-
using Advanced_PortChecker.Classes.Controls;
5+
using Advanced_PortChecker.Classes.Objects;
76

87
namespace Advanced_PortChecker.Classes.Scanner
98
{
109
/// <summary>
1110
/// Static class to determine whether a certain port is open or not
1211
/// </summary>
13-
internal static class PortChecker
12+
internal static class PortScanner
1413
{
1514
// ReSharper disable once InconsistentNaming
1615
/// <summary>
@@ -19,26 +18,23 @@ internal static class PortChecker
1918
/// <param name="address">The IP address that needs to be scanned</param>
2019
/// <param name="startPort">The starting point of ports that needs to be scanned</param>
2120
/// <param name="stopPort">The final port in a range of ports that needs to be scanned</param>
22-
/// <param name="timeout">The amount of time before the operation cancels</param>
23-
/// <param name="oi">The operation information regarding this scan</param>
24-
/// <returns>A list of information regarding the ports and address that was scanned</returns>
21+
/// <param name="timeout">The amount of time before a connection times out</param>
22+
/// <param name="scanOperation">The operation information regarding this scan</param>
23+
/// <returns>A list of LvCheck objects containing information regarding the ports and address that were scanned</returns>
2524
// ReSharper disable once IdentifierTypo
26-
internal static async Task<List<LvCheck>> CheckTCPUDP(string address, int startPort, int stopPort, int timeout, OperationInformation oi)
25+
internal static List<LvCheck> CheckTCPUDP(string address, int startPort, int stopPort, int timeout, ScanOperation scanOperation)
2726
{
2827
List<LvCheck> lv = new List<LvCheck>();
29-
await Task.Run(() =>
28+
for (int i = startPort; i <= stopPort; i++)
3029
{
31-
for (int i = startPort; i <= stopPort; i++)
32-
{
33-
if (oi.IsCancelled) return;
34-
35-
lv.AddRange(CheckTCP(address, i, i, timeout, oi, false).Result);
36-
lv.AddRange(CheckUDP(address, i, i, timeout, oi, false).Result);
30+
if (scanOperation.IsCancelled) break;
3731

38-
oi.Progress.Report(i);
39-
}
40-
});
32+
lv.AddRange(CheckTCP(address, i, i, timeout, scanOperation, false));
33+
lv.AddRange(CheckUDP(address, i, i, timeout, scanOperation, false));
4134

35+
scanOperation.Progress.Report(1);
36+
}
37+
scanOperation.ScanCompletedEvent?.Invoke();
4238
return lv;
4339
}
4440

@@ -49,37 +45,39 @@ await Task.Run(() =>
4945
/// <param name="address">The IP address that needs to be scanned</param>
5046
/// <param name="startPort">The starting point of ports that needs to be scanned</param>
5147
/// <param name="stopPort">The final port in a range of ports that needs to be scanned</param>
52-
/// <param name="timeout">The amount of time before the operation cancels</param>
53-
/// <param name="oi">The operation information regarding this scan</param>
48+
/// <param name="timeout">The amount of time before the connection times out</param>
49+
/// <param name="scanOperation">The operation information regarding this scan</param>
5450
/// <param name="reportProgress">A boolean to represent whether this method should report the current progress or not</param>
55-
/// <returns>A list of information regarding the ports and address that was scanned</returns>
56-
internal static async Task<List<LvCheck>> CheckTCP(string address, int startPort, int stopPort, int timeout, OperationInformation oi, bool reportProgress)
51+
/// <returns>A list of LvCheck objects containing information regarding the ports and address that were scanned</returns>
52+
internal static List<LvCheck> CheckTCP(string address, int startPort, int stopPort, int timeout, ScanOperation scanOperation, bool reportProgress)
5753
{
5854
List<LvCheck> lv = new List<LvCheck>();
59-
await Task.Run(() =>
55+
for (int i = startPort; i <= stopPort; i++)
6056
{
61-
for (int i = startPort; i <= stopPort; i++)
57+
if (scanOperation.IsCancelled) break;
58+
59+
LvCheck check = new LvCheck
6260
{
63-
if (oi.IsCancelled) return;
64-
65-
// ReSharper disable once UseObjectOrCollectionInitializer
66-
LvCheck check = new LvCheck
67-
{
68-
Address = address,
69-
Port = i,
70-
HostName = GetMachineNameFromIpAddress(address),
71-
Type = "TCP",
72-
Description = IsTcpOpen(address, i, timeout) ? "Open" : "Closed"
73-
};
74-
lv.Add(check);
75-
76-
if (reportProgress)
77-
{
78-
oi.Progress.Report(i);
79-
}
80-
oi.ItemProgress.Report(check);
61+
Address = address,
62+
Port = i,
63+
HostName = GetMachineNameFromIpAddress(address),
64+
Type = "TCP",
65+
Description = IsTcpOpen(address, i, timeout) ? "Open" : "Closed"
66+
};
67+
lv.Add(check);
68+
69+
if (reportProgress)
70+
{
71+
scanOperation.Progress.Report(1);
8172
}
82-
});
73+
scanOperation.ItemProgress.Report(check);
74+
}
75+
76+
if (reportProgress)
77+
{
78+
scanOperation.ScanCompletedEvent?.Invoke();
79+
}
80+
8381
return lv;
8482
}
8583

@@ -90,37 +88,38 @@ await Task.Run(() =>
9088
/// <param name="address">The IP address that needs to be scanned</param>
9189
/// <param name="startPort">The starting point of ports that needs to be scanned</param>
9290
/// <param name="stopPort">The final port in a range of ports that needs to be scanned</param>
93-
/// <param name="timeout">The amount of time before the operation cancels</param>
94-
/// <param name="oi">The operation information regarding this scan</param>
91+
/// <param name="timeout">The amount of time before the connection times out</param>
92+
/// <param name="scanOperation">The operation information regarding this scan</param>
9593
/// <param name="reportProgress">A boolean to represent whether this method should report the current progress or not</param>
96-
/// <returns>A list of information regarding the ports and address that was scanned</returns>
97-
internal static async Task<List<LvCheck>> CheckUDP(string address, int startPort, int stopPort, int timeout, OperationInformation oi, bool reportProgress)
94+
/// <returns>A list of LvCheck objects containing information regarding the ports and address that were scanned</returns>
95+
internal static List<LvCheck> CheckUDP(string address, int startPort, int stopPort, int timeout, ScanOperation scanOperation, bool reportProgress)
9896
{
9997
List<LvCheck> lv = new List<LvCheck>();
100-
await Task.Run(() =>
98+
for (int i = startPort; i <= stopPort; i++)
10199
{
102-
for (int i = startPort; i <= stopPort; i++)
100+
if (scanOperation.IsCancelled) break;
101+
102+
LvCheck check = new LvCheck
103+
{
104+
Address = address,
105+
Port = i,
106+
HostName = GetMachineNameFromIpAddress(address),
107+
Type = "UDP",
108+
Description = IsUdpOpen(address, i, timeout) ? "Open" : "Closed"
109+
};
110+
lv.Add(check);
111+
112+
if (reportProgress)
103113
{
104-
if (oi.IsCancelled) return;
105-
106-
// ReSharper disable once UseObjectOrCollectionInitializer
107-
LvCheck check = new LvCheck
108-
{
109-
Address = address,
110-
Port = i,
111-
HostName = GetMachineNameFromIpAddress(address),
112-
Type = "UDP",
113-
Description = IsUdpOpen(address, i, timeout) ? "Open" : "Closed"
114-
};
115-
lv.Add(check);
116-
117-
if (reportProgress)
118-
{
119-
oi.Progress.Report(i);
120-
}
121-
oi.ItemProgress.Report(check);
114+
scanOperation.Progress.Report(1);
122115
}
123-
});
116+
scanOperation.ItemProgress.Report(check);
117+
}
118+
119+
if (reportProgress)
120+
{
121+
scanOperation.ScanCompletedEvent?.Invoke();
122+
}
124123
return lv;
125124
}
126125

@@ -130,8 +129,8 @@ await Task.Run(() =>
130129
/// </summary>
131130
/// <param name="address">The IP address that needs to be scanned</param>
132131
/// <param name="port">The port that needs to be scanned</param>
133-
/// <param name="timeout">The amount of time before the operation cancels</param>
134-
/// <returns>Returns true if the port is open for connections</returns>
132+
/// <param name="timeout">The amount of time before the connection times out</param>
133+
/// <returns>Returns true if the port is open</returns>
135134
private static bool IsTcpOpen(string address, int port, int timeout)
136135
{
137136
try
@@ -156,8 +155,8 @@ private static bool IsTcpOpen(string address, int port, int timeout)
156155
/// </summary>
157156
/// <param name="address">The IP address that needs to be scanned</param>
158157
/// <param name="port">The port that needs to be scanned</param>
159-
/// <param name="timeout">The amount of time before the operation cancels</param>
160-
/// <returns>Returns true if the port is open for connections</returns>
158+
/// <param name="timeout">The amount of time before the connection times out</param>
159+
/// <returns>Returns true if the port is open</returns>
161160
private static bool IsUdpOpen(string address, int port, int timeout)
162161
{
163162
try
@@ -168,14 +167,21 @@ private static bool IsUdpOpen(string address, int port, int timeout)
168167
udpClient.Client.SendTimeout = timeout;
169168

170169
udpClient.Connect(address, port);
171-
return true;
170+
171+
byte[] sendBytes = new byte[4];
172+
new Random().NextBytes(sendBytes);
173+
udpClient.Send(sendBytes, sendBytes.Length);
174+
175+
IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
176+
177+
byte[] result = udpClient.Receive(ref remoteIpEndPoint);
178+
return result.Length != 0;
172179
}
173180
}
174181
catch (Exception)
175182
{
176-
// ignored
183+
return false;
177184
}
178-
return false;
179185
}
180186

181187
/// <summary>

Advanced PortChecker/Classes/Scanner/OperationInformation.cs renamed to Advanced PortChecker/Classes/Scanner/ScanOperation.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
using System;
2-
using Advanced_PortChecker.Classes.Controls;
2+
using Advanced_PortChecker.Classes.Objects;
33

44
namespace Advanced_PortChecker.Classes.Scanner
55
{
66
/// <summary>
77
/// Represents the content of a scan operation
88
/// </summary>
9-
internal sealed class OperationInformation
9+
internal sealed class ScanOperation
1010
{
1111
/// <summary>
1212
/// A boolean to indicate whether an operation was cancelled
@@ -20,5 +20,13 @@ internal sealed class OperationInformation
2020
/// The LvCheck item that is currently undergoing an operation
2121
/// </summary>
2222
internal IProgress<LvCheck> ItemProgress { get; set; }
23+
/// <summary>
24+
/// Delegate that can be called when the ScanOperation has completed its work
25+
/// </summary>
26+
internal delegate void ScanOperationCompleted();
27+
/// <summary>
28+
/// Event that can be used to indicate that a ScanOperation has completed its work
29+
/// </summary>
30+
internal ScanOperationCompleted ScanCompletedEvent;
2331
}
2432
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System.Collections.Generic;
2+
3+
namespace Advanced_PortChecker.Classes.Utils
4+
{
5+
/// <summary>
6+
/// Internal class to calculate the amount of actions that can to be performed per thread
7+
/// </summary>
8+
internal static class ThreadCalculator
9+
{
10+
/// <summary>
11+
/// Get the actions that can be performed per thread
12+
/// </summary>
13+
/// <param name="numberOfThreads">The number of threads that need to perform a certain amount of actions</param>
14+
/// <param name="numberOfActions">The number of actions that need to be performed by the threads</param>
15+
/// <returns>The number of actions that need to be performed per thread</returns>
16+
internal static IEnumerable<int> GetActionsPerThreads(int numberOfThreads, int numberOfActions)
17+
{
18+
// We have too many threads for the requested amount of actions
19+
while (numberOfActions < numberOfThreads)
20+
{
21+
numberOfThreads--;
22+
}
23+
24+
int[] actionsPerThread = new int[numberOfThreads];
25+
// Intentional loss of data
26+
int perThread = numberOfActions / numberOfThreads;
27+
int actionRemainder = numberOfActions - (numberOfThreads * perThread);
28+
29+
// No need to split anything. The threads can perform an equal amount of actions
30+
if (actionRemainder == 0)
31+
{
32+
for (int i = 0; i < numberOfThreads; i++)
33+
{
34+
actionsPerThread[i] = perThread;
35+
}
36+
}
37+
// We have more actions than we have threads. Time to reduce our thread count to the amount of actions
38+
else if (numberOfThreads > numberOfActions)
39+
{
40+
for (int i = 0; i < numberOfActions; i++)
41+
{
42+
actionsPerThread[i]++;
43+
}
44+
}
45+
// We have an unequal amount of actions per thread, time to split them
46+
else
47+
{
48+
// All threads perform the calculated amount of actions (rounded down)
49+
for (int i = 0; i < actionsPerThread.Length; i++)
50+
{
51+
actionsPerThread[i] = perThread;
52+
}
53+
54+
// Some tasks will have to do more work
55+
for (int i = 0; i < actionRemainder; i++)
56+
{
57+
actionsPerThread[i]++;
58+
}
59+
}
60+
return actionsPerThread;
61+
}
62+
}
63+
}

0 commit comments

Comments
 (0)