Подтвердить что ты не робот

Как рассчитать трафик исходящего и входящего интернет-трафика HttpWebRequest

У меня есть функция ниже, чтобы получить страницу. Мой вопрос: я хочу рассчитать, сколько интернет-трафика потрачено

Как входящий (загружаемый), так и исходящий трафик (отправленный)

Как я могу это сделать? Спасибо вам

Моя функция

 public static string func_fetch_Page(string srUrl, int irTimeOut = 60,
    string srRequestUserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:31.0) Gecko/20100101 Firefox/31.0",
    string srProxy = null)
    {
        string srBody = "";
        string srResult = "";
        try
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(srUrl);

            request.Timeout = irTimeOut * 1000;
            request.UserAgent = srRequestUserAgent;
            request.KeepAlive = true;
            request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";

            WebHeaderCollection myWebHeaderCollection = request.Headers;
            myWebHeaderCollection.Add("Accept-Language", "en-gb,en;q=0.5");
            myWebHeaderCollection.Add("Accept-Encoding", "gzip, deflate");

            request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;

            using (WebResponse response = request.GetResponse())
            {
                using (Stream strumien = response.GetResponseStream())
                {
                    using (StreamReader sr = new StreamReader(strumien))
                    {
                        srBody = sr.ReadToEnd();
                        srResult = "success";
                    }
                }
            }
        }
        catch ()
        {

        }

        return srBody;
    }

С#.net 4.5 Приложение WPF

@Simon Mourier как рассчитать потраченный трафик

public static long long_GlobalDownload_KByte = 0;
public static long long_GlobalSent_KByte = 0;

Time  _timer_fetch_download_upload = new Timer(getDownload_Upload_Values, null, 0, 100 * 1000);

public static void getDownload_Upload_Values(Object state)
{
    using (Process p = Process.GetCurrentProcess())
    {
        foreach (var cnx in TcpConnection.GetAll().Where(c => c.ProcessId == p.Id))
        {
            Interlocked.Add(ref long_GlobalDownload_KByte, Convert.ToInt64(cnx.DataBytesIn));
            Interlocked.Add(ref long_GlobalSent_KByte, Convert.ToInt64(cnx.DataBytesOut));
        }
    }
}
4b9b3361

Ответ 1

Вы можете создать прокси-сервер, используя FiddlerCore (всего лишь одну ссылку на dll) и установить WebProxy на свой HttpWebRequest, чтобы посчитать отправленный и полученные байты

public class WebConnectionStats
{
    static int _Read = 0;
    static int _Written = 0;

    public static void Init(bool registerAsSystemProxy = false)
    {
        Fiddler.FiddlerApplication.OnReadRequestBuffer += (s, e) => Interlocked.Add(ref _Written, e.iCountOfBytes);
        Fiddler.FiddlerApplication.OnReadResponseBuffer += (s, e) => Interlocked.Add(ref _Read, e.iCountOfBytes);
        Fiddler.FiddlerApplication.Startup(8088, registerAsSystemProxy, true);
    }

    public static int Read
    {
        get { return _Read; }
    }

    public static int Written
    {
        get { return _Written; }
    }
}

WebConnectionStats.Init(); //call this only once

var client = HttpWebRequest.Create("http://stackoverflow.com") as HttpWebRequest;
client.Proxy = new WebProxy("127.0.0.1", 8088);
var resp = client.GetResponse();
var html = new StreamReader(resp.GetResponseStream()).ReadToEnd();

Console.WriteLine("Read: {0}   Write: {1}", WebConnectionStats.Read, 
                                            WebConnectionStats.Written);

PS1: Этот показатель не будет включать длину заголовков Tcp

PS2: Вы можете получить более подробную информацию о ядре Fiddler здесь

Ответ 2

Один API Windows может предоставить вам эту информацию: GetPerTcpConnectionEStats для подключений IPV4 и связанной с ним функции GetPerTcp6ConnectionEStats IPV6. Обратите внимание, что вам нужно использовать SetPerTcpConnectionEStats, прежде чем получить какой-либо стат, и обычно требуется права администратора...

Чтобы получить список всех подключений, вы можете использовать функцию GetExtendedTcpTable. Он также может предоставить вам идентификатор процесса соединения, который очень полезен.

Это родные API, которые не так просты в использовании, но я создал класс TcpConnection, который обертывает все это. Он доступен здесь в небольшом WPF-приложении под названием IPStats: https://github.com/smourier/IPStats

Итак, сложность здесь заключается в том, чтобы связать ваш .NET HttpWebRequest с TcpConnection из списка подключений. Вы не можете получить какой-либо stat до того, как соединение существует, но как только вы создали соединение, вы можете получить соответствующий код с таким кодом:

    static IEnumerable<TcpConnection> GetProcessConnection(IPEndPoint ep)
    {
        var p = Process.GetCurrentProcess();
        return TcpConnection.GetAll().Where(c => ep.Equals(c.RemoteEndPoint) && c.ProcessId == p.Id);
    }

    HttpWebRequest req = ...

    // this is how you can get the enpoint, or you can also built it by yourself manually
    IPEndPoint remoteEndPoint;
    req.ServicePoint.BindIPEndPointDelegate += (sp, rp, rc) =>
        {
            remoteEndPoint = rp;
            return null;
        };
    // TODO: here, you need to connect, so the connection exists
    var cnx = GetProcessConnection(remoteEndPoint).FirstOrDefault();

    // access denied here means you don't have sufficient rights
    cnx.DataStatsEnabled = true;

    // TODO: here, you need to do another request, so the values are incremented
    // now, you should get non-zero values here
    // note TcpConnection also has int/out bandwidth usage, and in/out packet usage.
    Console.WriteLine("DataBytesIn:" + cnx.DataBytesIn);
    Console.WriteLine("DataBytesOut:" + cnx.DataBytesOut);

    // if you need all connections in the current process, just do this
    ulong totalBytesIn = 0;
    ulong totalBytesOut = 0;
    Process p = Process.GetCurrentProcess();
    foreach (var cnx in TcpConnection.GetAll().Where(c => c.ProcessId == p.Id))
    {
        totalBytesIn += cnx.DataBytesIn;
        totalBytesOut += cnx.DataBytesOut;
    }

Есть 3 недостатка:

  • вам нужно быть администратором, чтобы включить статистику данных;
  • Вы не можете получить статистику для самого первого запроса. Это может быть не проблема в зависимости от вашего контекст;
  • совпадение между соединением HttpWebRequest и TCP-соединением может оказаться более сложным, поскольку вы можете иметь более одного соединения с удаленной конечной точкой даже в том же процессе. Единственный способ отличить - определить локальную конечную точку (особенно порт) и расширить GetProcessConnection с этой локальной конечной точкой. К сожалению, нет простого способа сделать это. Вот ответ на этот вопрос: Как получить номер локального порта HttpWebRequest?

Обновление: если вы хотите постоянно контролировать все подключения для данного процесса, я написал класс утилиты ProcessTcpConnections, который запоминает все подключения и суммирует их использование. Он будет использоваться так (в примере приложения консоли):

class Program
{
    static void Main(string[] args)
    {
        ProcessTcpConnections p = new ProcessTcpConnections(Process.GetCurrentProcess().Id);
        Timer timer = new Timer(UpdateStats, p, 0, 100);

        do
        {
            // let activate the network so we measure something...
            using (WebClient client = new WebClient())
            {
                client.DownloadString("http://www.example.com");
            }
            Console.ReadKey(true); // press any key to download again
        }
        while (true);
    }

    private static void UpdateStats(object state)
    {
        ProcessTcpConnections p = (ProcessTcpConnections)state;
        p.Update();
        Console.WriteLine("DataBytesIn:" + p.DataBytesIn + " DataBytesOut:" + p.DataBytesOut);
    }
}

public class ProcessTcpConnections : TcpConnectionGroup
{
    public ProcessTcpConnections(int processId)
        : base(c => c.ProcessId == processId)
    {
        ProcessId = processId;
    }

    public int ProcessId { get; private set; }
}

public class TcpConnectionGroup
{
    private List<TcpConnectionStats> _states = new List<TcpConnectionStats>();
    private Func<TcpConnection, bool> _groupFunc;

    public TcpConnectionGroup(Func<TcpConnection, bool> groupFunc)
    {
        if (groupFunc == null)
            throw new ArgumentNullException("groupFunc");

        _groupFunc = groupFunc;
    }

    public void Update()
    {
        foreach (var conn in TcpConnection.GetAll().Where(_groupFunc))
        {
            if (!conn.DataStatsEnabled)
            {
                conn.DataStatsEnabled = true;
            }

            TcpConnectionStats existing = _states.Find(s => s.Equals(conn));
            if (existing == null)
            {
                existing = new TcpConnectionStats();
                _states.Add(existing);
            }
            existing.DataBytesIn = conn.DataBytesIn;
            existing.DataBytesOut = conn.DataBytesOut;
            existing.LocalEndPoint = conn.LocalEndPoint;
            existing.RemoteEndPoint = conn.RemoteEndPoint;
            existing.State = conn.State;
            existing.LastUpdateTime = DateTime.Now;
        }
    }

    public ulong DataBytesIn
    {
        get
        {
            ulong count = 0; foreach (var state in _states) count += state.DataBytesIn; return count;
        }
    }

    public ulong DataBytesOut
    {
        get
        {
            ulong count = 0; foreach (var state in _states) count += state.DataBytesOut; return count;
        }
    }

    private class TcpConnectionStats
    {
        public ulong DataBytesIn { get; set; }
        public ulong DataBytesOut { get; set; }
        public IPEndPoint LocalEndPoint { get; set; }
        public IPEndPoint RemoteEndPoint { get; set; }
        public TcpState State { get; set; }
        public DateTime LastUpdateTime { get;  set; }

        public bool Equals(TcpConnection connection)
        {
            return LocalEndPoint.Equals(connection.LocalEndPoint) && RemoteEndPoint.Equals(connection.RemoteEndPoint);
        }
    }
}

Ответ 3

Трудно точно узнать, сколько сетевого трафика происходит из-за непредсказуемого трафика Ethernet и тому подобного, но по существу суть любого HTTP-запроса:

method request-uri version
* (header : value)
CRLF
body

Учитывая это, вы можете рассчитать, как долго будет строка запроса:

HttpWebRequest req = ...;
StringBuilder requestText = new StringBuilder();

requestText.AppendFormat("{0} {1} HTTP/{2}.{3}", req.Method, req.RequestUri, req.ProtocolVersion.Major, req.ProtocolVersion.Minor);

requestText.AppendLine();

foreach (var header in req.Headers)
{
    requestText.AppendFormat("{0}: {1}", v, webReq.Headers[v]);
    requestText.AppendLine();
}

requestText.AppendLine();

// somehow add on the contents of the request stream, or just add that length later. I won't put that in this code because of stream positioning and all that

return System.Text.Encoding.UTF8.GetByteCount(requestText.ToString());

Тогда это очень похоже на сторону ответа. Формат ответа HTTP:

version status-code status-description
* (header : value)
CRLF
body

Итак,

HttpWebResponse resp = ...;
StringBuilder responseText = new StringBuilder();

responseText .AppendFormat("HTTP/{0}.{1} {2} {3}", resp.ProtocolVersion.Major, resp.ProtocolVersion.Minor, (int)resp.StatusCode, resp.StatusDescription);
responseText .AppendLine();

foreach (var header in resp.Headers)
{
    responseText .AppendFormat("{0}: {1}", v, resp.Headers[v]);
    responseText .AppendLine();
}

responseText.AppendLine();

Здесь немного решения. Вы должны получить длину тела ответа. Могут быть больше вариантов, но теперь я думаю, что вы могли:

  • Запишите обертку Stream, которая захватывает количество копируемых байтов. Таким образом, вам не нужно беспокоиться о передаче каналов, и это может быть интересно писать.
  • Используйте заголовок content-length, хотя тогда вы его не получите, если нет этого заголовка, как в случае transfer-encoding: chunked.
  • Прочитайте поток, используя любой метод, который вы хотите, затем добавьте его длину.
  • Скопируйте поток в MemoryStream, а затем передайте это как новый поток ответов, в то время как захватите длину на пути.

Что все сказано, у вас есть лишнее разочарование в том, что вы беспокоитесь о сжатии контента. Я вижу, вы используете его в своей, на самом деле. Учитывая простоту, я собираюсь взять GZip и пойти с четвертым вариантом. Возможно, вам захочется расширить то, что я пишу здесь, чтобы сделать его более полным.

// webReq.AutomaticDecompression = DecompressionMethods.None; is required for this, since we're handling that decompression ourselves.

using (var respStream = resp.GetResponseStream())
using (var memStream = new MemoryStream())
{
    respStream.CopyTo(memStream);

    using (var gzip = new System.IO.Compression.GZipStream(respStream, System.IO.Compression.CompressionMode.Decompress))
    using (var reader = new StreamReader(gzip))
    {
        var content = reader.ReadToEnd();

        // you may or may not actually care about this, depending on whether this is just light testing or if you'll actually have these metrics in production
    }

    return System.Text.Encoding.UTF8.GetByteCount(responseText.ToString()) + memStream.Length;
}

Теперь несколько предостережений, которые я вижу сейчас, когда смотрю. Другие могут заметить больше, и я добавлю их, если люди прокомментируют.

  • Как я уже говорил в начале этого, с одного запроса может быть больше сетевого трафика, чем это вам скажет.
  • В зависимости от клиента и сервера вы можете обнаружить, что фактические заголовки могут быть перечислены с другим количеством пробелов. Я не считаю, что за пределами спецификации HTTP, и даже если это люди, это сделает это. Итак, случайный пример, что вы увидите один серверный набор content-type: text/html и другой набор content-type : text/html. Там по крайней мере (ну, точно, поскольку мы используем UTF-8) разницу в один байт. Опять же, мало, но это возможное расхождение.

Это просто оценка. Это хороший, но это просто оценка.

Если вы ищете дополнительную точность за счет некоторой простоты, вы также можете написать себе прокси. Идея прокси-сервера HTTP заключается в том, что он просто получает дословный запрос, поэтому он "супер легко", чтобы получить длину. Самой большой проблемой, конечно же, является то, что вам нужно написать полностью функционирующий прокси-сервер, который, по всей вероятности, анализирует и повторно запрашивает ваши входящие запросы, а также анализирует и передает все свои ответы. Конечно, выполнимо, но это нетривиально. В частности, если вам нужно беспокоиться о SSL.

На самом базовом уровне, конечно, вы могли бы написать, по сути, альтернативу программе, например WireShark. Я не знаю, как это сделать, и я бы не посоветовал вам беспокоиться, если вам это действительно нужно. Это все равно не даст вам идеальной идеи. Только ближе.

И теперь, все сказанное и мальчик, это было немного сказано, если вы делаете это для профилирования, есть хороший шанс, что инструменты, встроенные в Visual Studio, позволят вам это сделать. По общему признанию, я далек от знания на них - я их даже не открывал, но я считаю, что есть профилировщики сетевого трафика. Конечно, я сомневаюсь, что они будут работать на каждой такой платформе. Но это, безусловно, было бы более легким подходом.

Кроме того, если кто-то случайно заметил какие-либо опечатки, я несколько раз копировал-вставлял пару раз, и я думаю, что я получил их все, но не стесняйтесь сообщить мне или просто исправить их.

Ответ 4

он бит уродливый, но вы можете использовать трассировку сети и затем анализировать журнал. см. http://msdn.microsoft.com/en-us/library/ty48b824(v=vs.110).aspx для включения сетевой трассировки

Я добавляю базовый код (я знаю, что у меня ошибка в анализе журнала, но идея должна быть понятной)

NetworkListner.cs

using System;
using System.Diagnostics;
using System.Globalization;
using System.Text.RegularExpressions;

namespace NetworkTracing
{
    /// <summary>
    /// Description of NetworkListner.
    /// </summary>
    public class NetworkListner : TraceListener
    {
        public static int BytesSent { get; private set;} 
        public static int BytesReceived { get; private set;}
        private bool _inSend = false;
        private bool _inReceived = false;
        private string _lastMessage = "";
        private Regex _lengthRegex = new Regex(@"(\[\d*\]) ([\dA-F]*)");
        public NetworkListner()
        {
            BytesSent = 0;
            BytesReceived = 0;          
        }

        private int ExtractNumOfBytes(string message){
            string lengthUntilThisLineStr = null;
            try {           
                var match = _lengthRegex.Match(message);                
                lengthUntilThisLineStr = match.Groups[2].Value;
            } catch (ArgumentException ex) {
                // Syntax error in the regular expression
            }
            if (String.IsNullOrEmpty(lengthUntilThisLineStr)) {
                return 0;
            }
            var lengthUntilThisLine = int.Parse(lengthUntilThisLineStr,NumberStyles.HexNumber);
            return lengthUntilThisLine;
        }

        public override void Write(string message) {
            if (message.Equals("System.Net.Sockets Verbose: 0 : ")) {
                return;
            }
            if (message.Contains("Exiting Socket#")) {
                int bytes = ExtractNumOfBytes(_lastMessage);
                if (_inSend) {
                    _inSend = false;
                    BytesSent += bytes;
                }else if (_inReceived) {
                    _inReceived = false;
                    BytesReceived += bytes;
                }   
            }           
            else if (message.Contains("Data from Socket")){
                if (message.Contains("Send")) {
                    _inSend = true;
                }
                else if (message.Contains("Receive")) {
                    _inReceived = true;
                }
            }
            _lastMessage = message;
        }

        public override void WriteLine(string message) {
            Write(message + Environment.NewLine);
        }
    }
}

Program.cs

using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Net;

namespace NetworkTracing
{
    class Program
    {
        public static void Main(string[] args)
        {           
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.google.com");
            request.UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:31.0) Gecko/20100101 Firefox/31.0";
            using (WebResponse response = request.GetResponse())
            {
                using (Stream strumien = response.GetResponseStream())
                {
                    using (StreamReader sr = new StreamReader(strumien))
                    {
                        var res = sr.ReadToEnd();
                        Console.WriteLine("Send -> {0}",NetworkListner.BytesSent);
                        Console.WriteLine("Recieve -> {0}",NetworkListner.BytesReceived);
                        Console.ReadLine();
                    }
                }
            }
        }
    }

App.config

<configuration>
<system.diagnostics>
    <sources>
      <source name="System.Net.Sockets" tracemode="includehex" maxdatasize="1024">
        <listeners>
          <add name="network"/>
        </listeners>
      </source>
    </sources>
     <switches>
      <add name="System.Net.Sockets" value="Verbose"/>      
    </switches>
    <sharedListeners>
      <add name="network"
        type="NetworkTracing.NetworkListner, NetworkTracing"
      />
    </sharedListeners>
    <trace autoflush="true"/>
  </system.diagnostics>
</configuration>

вам также нужно скомпилировать свой проект с флагом трассировки (этот флаг по умолчанию используется для отладки):

  • С проектом, выбранным в обозревателе решений, в меню "Проект"
  • нажмите "Свойства".
  • Перейдите на вкладку "Скомпилировать".
  • Нажмите кнопку "Дополнительные параметры компиляции", чтобы открыть расширенный Диалоговое окно "Параметры компилятора".
  • Установите флажок "Определить константу TRACE" и нажмите "ОК".

Ответ 5

Если ваше клиентское приложение взаимодействует с вашим известным сервером IIS, я бы попытался получить эти данные из журналов IIS. Проверьте cs-байты (байты клиента на сервер байты aka request) и sc-байты (от сервера к байтам клиента или ответ). http://technet.microsoft.com/en-us/library/cc754702(v=ws.10).aspx

Ответ 6

Я думаю, вы можете использовать счетчик производительности .NET CLR Networking, поскольку он может дать вам отправленные и полученные байты на AppDomain

http://msdn.microsoft.com/en-us/library/70xadeyt%28v=vs.110%29.aspx

Я написал класс Helper для проверки точности, и результаты похожи на Windows ResourceMonitor, поэтому я считаю, что точность должна быть приемлемой.

Как использовать:

вот класс Helper:

using System.Diagnostics;
using System.Linq;

public class NetworkMonitor
{
    private PerformanceCounter _bytesSent;
    private PerformanceCounter _bytesReceived;
    private readonly int _processId;
    private bool _initialized;

    public NetworkMonitor(int processID)
    {
        _processId = processID;
        Initialize();
    }

    public NetworkMonitor()
        : this(Process.GetCurrentProcess().Id)
    {

    }
    private void Initialize()
    {
        if (_initialized)
            return;

        var category = new PerformanceCounterCategory(".NET CLR Networking 4.0.0.0");
        var instanceNames = category.GetInstanceNames().Where(i => i.Contains(string.Format("p{0}", _processId)));
        if (!instanceNames.Any()) return;

        _bytesSent = new PerformanceCounter
        {
            CategoryName = ".NET CLR Networking 4.0.0.0",
            CounterName = "Bytes Sent",
            InstanceName = instanceNames.First(),
            ReadOnly = true
        };

        _bytesReceived = new PerformanceCounter
        {
            CategoryName = ".NET CLR Networking 4.0.0.0",
            CounterName = "Bytes Received",
            InstanceName = instanceNames.First(),
            ReadOnly = true
        };

        _initialized = true;
    }

    public float GetSentBytes()
    {
        Initialize(); //in Net4.0 performance counter will get activated after first request
        return _initialized ? _bytesSent.RawValue : 0;
    }
    enter code here
    public float GetReceivedBytes()
    {
        Initialize(); //in Net4.0 performance counter will get activated after first request
        return _initialized ? _bytesReceived.RawValue : 0;
    }
} 

вы должны добавить эту часть в свой app.config

  <system.net>
    <settings>
      <performanceCounters enabled="true" />
    </settings>
  </system.net>

и вот образец, который я использовал для проверки точности на основе вашего собственного метода:

   private static void Main(string[] args)
        {
            var netMonitor = new NetworkMonitor();

            var received = netMonitor.GetReceivedBytes();
            var sent = netMonitor.GetSentBytes();

            Console.WriteLine("received:{0}, sent:{1}", received, sent);
            func_fetch_Page("http://www.google.com");

            received = netMonitor.GetReceivedBytes();
            sent = netMonitor.GetSentBytes();

            Console.WriteLine("received:{0}, sent:{1}", received, sent);
            Console.ReadKey();
        }