Как рассчитать 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;
}
приложение C# .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));
}
}
}
6 ответов
Вы можете создать прокси-сервер, используя в описании 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 core здесь
один API Windows может дать вам эту информацию:GetPerTcpConnectionEStats для соединений IPV4 и связанной функции GetPerTcp6ConnectionEStats IPV6. Обратите внимание, что вам нужно использовать SetPerTcpConnectionEStats прежде чем получить какой-либо стат, и это обычно требует прав администратора...
чтобы получить список всех соединений, вы можете использовать GetExtendedTcpTable
трудно сказать ровно сколько сетевого трафика происходит из-за какого-то непредсказуемого трафика 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
оболочка, которая захватывает количество скопированных байтов. Таким образом, вам не нужно беспокоиться о чанковых передачах, и это может быть весело писать. - использовать , хотя тогда вы не получите его, если есть не этот заголовок, как в случае с
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 я позволю тебе сделать это. Конечно, я далек от того, чтобы разбираться в них-я даже никогда не открывал их, - но я верю, что есть профилировщики сетевого трафика. Конечно, я сомневаюсь, что они работают на любой платформе, как это было. Но это, безусловно, было бы более легким подходом.
кроме того, если кто-нибудь заметит какие-либо опечатки здесь, я сделал копию-вставить пару раз, и я думаю, что я получил их все, но не стесняйтесь, дайте мне знать или просто исправить их.
это немного уродливо, но вы можете использовать трассировку сети, а затем проанализировать журнал. см.http://msdn.microsoft.com/en-us/library/ty48b824 (v=против 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);
}
}
}
Если ваше клиентское приложение взаимодействует с известными серверами IIS, я бы попытался получить эти данные из журналов IIS. Проверьте cs-байты (клиент-сервер байты aka байты запроса) и sc-байты (сервер-клиент байты aka ответ). http://technet.microsoft.com/en-us/library/cc754702 (v=ws.10).aspx
Я думаю, вы можете использовать .NET CLR Networking счетчик производительности, поскольку он может дать вам отправленные и полученные байты на AppDomain
http://msdn.microsoft.com/en-us/library/70xadeyt%28v=vs.110%29.aspx
Я написал вспомогательный класс для проверки точности, и результаты похожи на Windows ResourceMonitor, поэтому я считаю, что точность должна быть приемлемой.
как использовать:
вот помощник класс:
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;
}
}
вы должны добавить эту часть в свое приложение.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();
}