Missing network sent/received

2019-06-11 11:40发布

问题:

I am following the answer here:

Calculating Bandwidth

And implemented everything like he said. My monitor is initialized like so:

netSentCounter.CategoryName = ".NET CLR Networking";
netSentCounter.CounterName = "Bytes Sent";
netSentCounter.InstanceName = Misc.GetInstanceName();
netSentCounter.ReadOnly = true;

I can corrently see that Misc.GetInstanceName() returns "MyProcessName[id]". However I keep getting the exception that the instance doesn't exist in the specified category.

My understanding is that the category for net sent/received isn't created until you actually send or receive.

I have added the app.config as described in the answer like so:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.net>
        <settings>
            <performanceCounters enabled="true" />
        </settings>
    </system.net>
</configuration>

Why do I still get an error?

Here is my monitoring code:

public static class Monitoring
{
    private static PerformanceCounter netSentCounter = new PerformanceCounter();

    //Static constructor
    static Monitoring()
    {
        netSentCounter.CategoryName = ".NET CLR Networking";
        netSentCounter.CounterName = "Bytes Sent";
        netSentCounter.InstanceName = Misc.GetInstanceName();
        netSentCounter.ReadOnly = true;
    }

    /// <summary>
    /// Returns the amount of data sent from the current application in MB
    /// </summary>
    /// <returns></returns>
    public static float getNetSent()
    {
        return (float)netSentCounter.NextValue() / 1048576; //Convert to from Bytes to MB
    }
}

And my Misc class:

public static class Misc
{

    //Returns an instance name
   internal static string GetInstanceName()
    {
        // Used Reflector to find the correct formatting:
        string assemblyName = GetAssemblyName();
        if ((assemblyName == null) || (assemblyName.Length == 0))
        {
            assemblyName = AppDomain.CurrentDomain.FriendlyName;
        }
        StringBuilder builder = new StringBuilder(assemblyName);
        for (int i = 0; i < builder.Length; i++)
        {
            switch (builder[i])
            {
                case '/':
                case '\\':
                case '#':
                    builder[i] = '_';
                    break;
                case '(':
                    builder[i] = '[';
                    break;

                case ')':
                    builder[i] = ']';
                    break;
            }
        }
        return string.Format(CultureInfo.CurrentCulture,
                             "{0}[{1}]",
                             builder.ToString(),
                             Process.GetCurrentProcess().Id);
    }

    /// <summary>
    /// Returns an assembly name
    /// </summary>
    /// <returns></returns>
    internal static string GetAssemblyName()
    {
        string str = null;
        Assembly entryAssembly = Assembly.GetEntryAssembly();
        if (entryAssembly != null)
        {
            AssemblyName name = entryAssembly.GetName();
            if (name != null)
            {
                str = name.Name;
            }
        }
        return str;
    }
 }

Edit: I have opened the resource monitor from windows to see what the problem is. The counter isn't being started albeit having the app.config set to do so.

Here is what i see (this before and after my application sending network activity)

And the name isn't what my method is returning. My method returns "SuperScraper[appId]" while in the resource it is called "Superscraper.vshost.exe".

So I have two problems now:

-My counter isn't starting on app startup -The name is differnet

回答1:

Ok I have finally figured it out. The steps listed Calculating Bandwidth seem to be outdated and are no longer valid for .Net 4.

I am going to explain the whole process so you can do it aswell.

First off, you need to add this to your app.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.net>
        <settings>
            <performanceCounters enabled="true" />
        </settings>
    </system.net>
</configuration>

Before .Net 4.0, this made the application create the counters on startup (and not for example, create the network counters when your application sends a request). In .Net 4.0, this tells it to create the counters when they are used. Ie. if you don't set this no counters will ever be created.

Now this is where I wasted most of my time. I was under the wrong assumption that if you create a performance counter with the same name that it would have had if it was created naturally, you would get the values. However all this does is block the real counter from showing up.

So doing this:

//In .Net 4.0 the category is called .NET CLR Networking 4.0.0.0 and not .NET CLR Networking
netSentCounter.CategoryName = ".NET CLR Networking 4.0.0.0";
netSentCounter.CounterName = "Bytes Sent";
netSentCounter.InstanceName = Misc.GetInstanceName();
netSentCounter.ReadOnly = false; //<==
netSentCounter.RawValue = 0;     //<==

Simply blocks the real counter.

What you need to do is start it up in a natural way. The best way to do this is to simply send a spoof request at application start, and then "listen" to the performance counter using:

//Send spoof request here
netSentCounter.CategoryName = ".NET CLR Networking 4.0.0.0";
netSentCounter.CounterName = "Bytes Sent";
netSentCounter.InstanceName = Misc.GetInstanceName();
netSentCounter.ReadOnly = true;

One final point. The instance name is no longer ApplicationName[appId]. Now it is:

[ApplicationName].exe_p[appId]_r[the CLR id hosting your application]_ad[ApplicationDomain]

Hope this saves someone time!!



回答2:

Ok I tried your example. I also expierenced that counter didnt showed in Performance Monitor, but it did after I changed ReadOnly and set RawValue properties:

netSentCounter.CategoryName = ".NET CLR Networking";
netSentCounter.CounterName = "Bytes Sent";
netSentCounter.InstanceName = Misc.GetInstanceName();
netSentCounter.ReadOnly = false; //<==
netSentCounter.RawValue = 0;     //<==

In my case I found that instance name in performance monitor is in this format: myapplicationname_pPID

So after I changed in your GetInstanceName method line

return string.Format(CultureInfo.CurrentCulture,
                         "{0}[{1}]",
                         builder.ToString(),
                         Process.GetCurrentProcess().Id);

to

return string.Format(CultureInfo.CurrentCulture,
                     "{0}_p{1}",                
                     builder.ToString().ToLower(), //<== dont miss ToLower()
                     Process.GetCurrentProcess().Id);

counter looks like started to work.

Here is reference how to add and remove counter instance

Also think about removing(netSentCounter.RemoveInstance()) counter after you finish to use it.