Netzwerkauslasung auslesen

Seit Windows 8 gibt es über den TaskManager sehr nützliche Auswertungen zur Auslastung von CPU, RAM, Festplatten sowie dem Netzwerk. Diese in C# nachzubauen ist gar nichtmal schwer, da es im .NET-Framework bereits dafür vorgesehene Klassen und somit Implementierungen gibt. Im Falle der Netzwerkauslastung ist es jedoch so, dass keine Zuordnung zwischen IP-Adressen und Auslastung besteht wie es im TaskManager der Fall ist. Um diese Erweiterung zu implementieren, muss man im Rahmen des .NET-Frameworks einen kleinen Umweg bemühen. Ethernet-Performance-Win8

Zunächst einmal, um hier eine saubere Implementierung hinbekommen zu können, muss ein wenig Grundwissen im Bereich Regex vorhanden sein. Außerdem schadet es nicht, die Klassen NetworkInterface sowie PerformanceCounter aus dem .NET-Framework ein wenig zu kennen.

In Zusammenhang mit den beiden oben genannten Klassen und ein wenig Regex lässt sich das Vorhaben recht schnell implementieren. Warum wir Regex an dieser Stelle benötigen? Nun, Ethernet-Schnittstellen, die Sonderzeichen wie Klammern enthalten (siehe Bild), werden in der einen Schnittstelle mit den richtigen Zeichen dargestellt, in der anderen jedoch durch andere Zeichen ersetzt. In dem Fall der Schnittstelle aus der Grafik würde uns NetworkInterface ein „Gigabit-Netzwerkverbindung Intel(R) 82579V“ liefern, während uns der PerformanceCounter ein „Gigabit-Netzwerkverbindung Intel[R] 82579V“ als Ergebnis liefert.

Um diese Unterschiede auseinander halten zu können, verwenden wir in C# ein Regex-Replace, das wir über die Regex „\W+“ schieben. Implementieren wir uns also zunächsteinmal eine Methode, die dafür sorgt, dass wir eine Zusammengehörigkeit zwischen NetworkInterface und PerformanceCounter feststellen können. Diese „Validierungs“-Methode sieht bei mir wie folgt aus:

private bool IsValid(string performanceCounerInstance, string networkInterfaceDescription)
{
	Regex regex = new Regex(@"\W+");
	string cleanPerformanceCounterInstance = regex.Replace(performanceCounerInstance, string.Empty);
	string cleanNetworkInterfaceName = regex.Replace(networkInterfaceDescription, string.Empty);
	
	return cleanPerformanceCounterInstance == cleanNetworkInterfaceName;
}

Nun können wir dazu übergehen zwei Schleifen zu definieren, die uns einmal alle X-Millisekunden die Auslastung für Schnittstelle A liefern. Aber bevor wir dies tun, definieren wir uns noch eine Methode, um die Ausgabe lesbarer zu gestalten. Diese soll nichts weiter tun, als die am Ende von PerformanceCounter gemessenen Werte in ein z.B. Mbit/s-Wetz zu wandeln.

private string FormatDataRate(float value)
{
	float dataRateBits = value * 8;	
	if (dataRateBits > 1073741824)
		return string.Format("{0:F2} GBit/s", dataRateBits / 1073741824);	
	if (dataRateBits > 1048576)
		return string.Format("{0:F2} MBit/s", dataRateBits / 1048576);	
	if (dataRateBits > 1024)
		return string.Format("{0:F2} KBit/s", dataRateBits / 1024);
	
	return string.Format("{0:F2} Bit/s", dataRateBits);
}

Nun, da wir auch diese Methode haben, können wir endlich mit der eigentlichen Implementierung beginnen. Hier benötigen wir zunächst eine PerformanceCounterCategory mit dem Parameter „Network Interface“. Anschließend können wir  mittels foreach-Schleife, die ihre Daten aus „NetworkInterface.GetAllNetworkInterfaces()“ bekommt, unsere Schnittstellen durchlaufen. Hier müssen wir immer dann, wenn der Schnittstellentyp „Wireless80211“ (WLAN 802.11) oder „Ethernet“ (LAN-Kabel) ist, unsere Ausgabe tätigen. Für die Ausgabe der IP-Adresse der jeweiligen Schnittstelle benötigen wir die Funktionalität „networkInterface.GetIPProperties().UnicastAddresses“ unserer Schnittstelle. Wenn wir dies haben, können wir anschließend die existierenden PerformanceCounter mittels foreach-Schleife durchlaufen. Diese bekommen wir über die Methode „performanceCounterCategory.GetInstanceNames()„. Hier müssen wir uns nun zwei neue PerformanceCounter erstellen. Der erste, für Download Messungen, bekommt folgende drei Parameter: „Network Interface“, „Bytes Received/sec“, instance – wobei instance hier die Instanz des jeweiligen Counters ist. Beim zweiten, für die Upload Messungen, benutzen wir folgende drei Parameter: „Network Interface“, „Bytes Sent/sec“, instance. Nun sollte unsere Applikation in etwa wie folgt aussehen:

void Main()
{
	PerformanceCounterCategory performanceCounterCategory = new PerformanceCounterCategory("Network Interface");
	foreach(NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces())
	{
		if(networkInterface.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 || 
		   networkInterface.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
		{
			Console.WriteLine(networkInterface.Name);
			Console.WriteLine(networkInterface.Description);
			foreach (UnicastIPAddressInformation ip in networkInterface.GetIPProperties().UnicastAddresses)
			{
				if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
					Console.WriteLine(ip.Address.ToString());
			}
			
			foreach (string instance in performanceCounterCategory.GetInstanceNames()) 
			{
				if (IsValid(instance, networkInterface.Description)) 
				{
					PerformanceCounter receivedCounter = 
						new PerformanceCounter("Network Interface", "Bytes Received/sec", instance);
						
					PerformanceCounter sentCounter = 
						new PerformanceCounter("Network Interface", "Bytes Sent/sec", instance);
					
					Console.WriteLine(string.Format("Received: {0}", FormatDataRate(receivedCounter.NextValue())));
					Console.WriteLine(string.Format("Sent: {0}", FormatDataRate(sentCounter.NextValue())));
				}
			}
			
			Console.WriteLine();
		}  
	}
}

private bool IsValid(string performanceCounerInstance, string networkInterfaceDescription)
{
	Regex regex = new Regex(@"\W+");
	string cleanPerformanceCounterInstance = regex.Replace(performanceCounerInstance, string.Empty);
	string cleanNetworkInterfaceName = regex.Replace(networkInterfaceDescription, string.Empty);
	
	return cleanPerformanceCounterInstance == cleanNetworkInterfaceName;
}

private string FormatDataRate(float value)
{
	float dataRateBits = value * 8;	
	if (dataRateBits > 1073741824)
		return string.Format("{0:F2} GBit/s", dataRateBits / 1073741824);	
	if (dataRateBits > 1048576)
		return string.Format("{0:F2} MBit/s", dataRateBits / 1048576);	
	if (dataRateBits > 1024)
		return string.Format("{0:F2} KBit/s", dataRateBits / 1024);
	
	return string.Format("{0:F2} Bit/s", dataRateBits);
}

Beim Ausführen erhalten wir nun z.B. eine solche Ausgabe:

LAN-Verbindung
TAP-Windows Adapter V9
169.254.240.43

Ethernet
Gigabit-Netzwerkverbindung Intel(R) 82579V
10.10.11.50
Received: 8,77 MBit/s
Sent: 2,46 KBit/s

VirtualBox Host-Only Network
VirtualBox Host-Only Ethernet Adapter
192.168.56.1
Received: 0,00 Bit/s
Sent: 0,00 Bit/s

Ich hoffe damit konnte ich wieder einigen helfen! 🙂