c# - example - Cómo calcular HttpWebRequest gastado el tráfico de Internet entrante y saliente
webrequest post c# json (6)
Tengo la siguiente función para recuperar una página. Mi pregunta es: quiero calcular la cantidad de conexión a Internet que se gasta.
Tráfico entrante (descarga) y saliente (enviado)
Cómo puedo hacer eso ? Gracias
Mi funcion
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;
}
Aplicación C # .net 4.5 WPF
@Simon Mourier, ¿cómo calculo el tráfico gastado?
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));
}
}
}
Creo que puede usar el contador de rendimiento de .NET CLR Networking , ya que puede darle bytes enviados y recibidos por AppDomain
http://msdn.microsoft.com/en-us/library/70xadeyt%28v=vs.110%29.aspx
Escribí una clase de ayudante para verificar la precisión y los resultados son similares a Windows ResourceMonitor, por lo que creo que la precisión debería ser aceptable.
Cómo utilizar:
Aquí está la clase de ayuda:
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;
}
}
debe agregar esta parte a su app.config
<system.net>
<settings>
<performanceCounters enabled="true" />
</settings>
</system.net>
Y aquí está la muestra que utilicé para verificar la precisión según su propio método:
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();
}
Es difícil saber exactamente cuánto tráfico de red se está produciendo debido a un poco de tráfico de Ethernet impredecible y ese tipo de cosas, pero esencialmente la esencia de cualquier solicitud HTTP es:
method request-uri version
* (header : value)
CRLF
body
Dado eso, puedes calcular cuánto tiempo será la cadena de solicitud:
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());
Entonces es bastante similar para el lado de la respuesta. El formato para una respuesta HTTP es:
version status-code status-description
* (header : value)
CRLF
body
Asi que,
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();
Aquí, hay una pequeña decisión que tomar. Necesitas obtener la longitud del cuerpo de respuesta. Puede haber más opciones, pero lo que estoy pensando ahora es que usted podría:
- Escriba un contenedor de
Stream
que capture el número de bytes copiados. De esa manera, no tiene que preocuparse por las transferencias fragmentadas, y podría ser algo divertido escribir. - Utilice el encabezado de
content-length
delcontent-length
, aunque entonces no lo obtendrá si no hay ese encabezado, como es el caso detransfer-encoding: chunked
detransfer-encoding: chunked
. - Lea la secuencia usando el método que desee, luego agregue la longitud de eso.
- Copie el flujo a un
MemoryStream
, luegoMemoryStream
como el nuevo flujo de respuesta mientras agarra la longitud en el camino.
Dicho todo esto, tiene la frustración adicional de preocuparse por la compresión de contenido. Veo que lo usas en el tuyo, de hecho. Dada la simplicidad, asumiré GZip e iré con la cuarta opción. Es posible que desee ampliar lo que escribo aquí para hacerlo un poco más completo.
// 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;
}
Ahora, algunas advertencias que estoy viendo como veo ahora. Otros pueden notar más, y los agregaré si la gente comenta.
- Como mencioné al comienzo de esto, puede haber más tráfico de red en una solicitud de lo que esto le dirá.
- Dependiendo del cliente y del servidor, podría encontrar que los encabezados reales podrían aparecer en una lista con un número diferente de espacios. No creo que esté más allá de la especificación HTTP, e incluso si es gente, lo hará. Por lo tanto, existe la posibilidad, por ejemplo, de que vea un
content-type: text/html
conjunto de servidorescontent-type: text/html
y otrocontent-type : text/html
conjuntocontent-type : text/html
. Hay al menos (bueno, exactamente, ya que estamos usando UTF-8) una diferencia de un byte. De nuevo, poco, pero esa es una posible discrepancia.
Esto es sólo una estimación. Es una buena, pero es solo una estimación.
Si busca precisión adicional a costa de cierta simplicidad, también puede escribirse un proxy. La idea de un proxy HTTP es que simplemente recibe la solicitud literal, por lo que es "muy fácil" obtener la longitud. El mayor problema, por supuesto, es que luego tiene que escribir un proxy completamente funcional que, con toda probabilidad, analice y vuelva a solicitar sus solicitudes entrantes, y analice y transmita todas sus respuestas respectivas. Ciertamente es factible, pero no es trivial. Particularmente si tienes que preocuparte por SSL.
En el nivel más básico, por supuesto, podría escribir, en esencia, una alternativa a un programa como WireShark. No sabría cómo hacerlo, y no sugeriría que te molestes a menos que realmente lo necesites. Eso tampoco te daría una idea perfecta. Sólo más cerca.
Y ahora, todo lo dicho, y chico, eso fue bastante dicho, si haces esto con el propósito de crear perfiles, es muy probable que las herramientas integradas en Visual Studio te permitan hacerlo. Es cierto que estoy muy lejos de tener conocimientos sobre ellos, nunca los he abierto, pero creo que hay perfiles de tráfico de red disponibles. Por supuesto, dudo que funcionen en todas las plataformas como lo haría. Pero eso ciertamente sería un enfoque más fácil.
Además, si alguien nota algún error tipográfico aquí, copié y pegué un par de veces y creo que los obtuve todos, pero siéntase libre de avisarme o simplemente corregirlos.
Es un poco feo, pero puedes usar el rastreo de la red y luego analizar el registro. vea http://msdn.microsoft.com/en-us/library/ty48b824(v=vs.110).aspx para habilitar el rastreo de red
agrego código básico (sé que tengo un error al analizar el registro pero la idea debería ser clara)
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>
también necesita compilar su proyecto con un indicador de seguimiento (este indicador está predeterminado en depuración) por:
- Con un proyecto seleccionado en el Explorador de soluciones, en el menú Proyecto
- haga clic en Propiedades.
- Haga clic en la pestaña Compilar.
- Haga clic en el botón Opciones de compilación avanzadas para abrir el cuadro de diálogo Configuración del compilador avanzado.
- Seleccione la casilla de verificación Definir constante de TRACE y luego haga clic en Aceptar.
Puede crear un proxy usando FiddlerCore (solo una sola dll para referencia) y configurar un WebProxy para su HttpWebRequest para contar los bytes enviados y recibidos
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://.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: esta cuenta no incluirá la longitud de los encabezados de Tcp
PS2: Puedes obtener más información sobre el núcleo de Fiddler here
Si su aplicación cliente se está comunicando con su (s) servidor (es) IIS conocido (s), entonces intentaría obtener estos datos de los registros de IIS. Verifique los cs-bytes (bytes del cliente al servidor, también conocidos como bytes de solicitud) y sc-bytes (bytes del servidor al cliente, también conocido como respuesta). http://technet.microsoft.com/en-us/library/cc754702(v=ws.10).aspx
Una API de Windows puede proporcionarle esta información: GetPerTcpConnectionEStats para conexiones IPV4 y la función asociada GetPerTcp6ConnectionEStats de IPV6. Tenga en cuenta que necesita usar SetPerTcpConnectionEStats antes de poder obtener cualquier estadística, y que generalmente necesita derechos de administrador ...
Para obtener la lista de todas las conexiones, puede usar la función GetExtendedTcpTable . También puede proporcionarle el ID de proceso de la conexión, que es muy útil.
Estas API nativas no son tan fáciles de usar, pero he creado una clase TcpConnection que envuelve todo esto. Está disponible aquí en una pequeña aplicación WPF llamada IPStats: https://github.com/smourier/IPStats
Entonces, la dificultad aquí es vincular su .NET HttpWebRequest a una TcpConnection desde la lista de conexiones. No puede obtener ninguna estadística antes de que exista la conexión, pero una vez que haya creado la conexión, puede obtener la correspondiente con un código como este:
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;
}
Hay 3 inconvenientes:
- necesitas ser administrador para habilitar estadísticas de datos;
- no puede obtener las estadísticas para la primera solicitud. Esto puede no ser un problema dependiendo de su contexto;
- la coincidencia entre la conexión de HttpWebRequest y la conexión TCP puede ser más difícil de determinar porque puede tener más de una conexión a un punto final remoto, incluso en el mismo proceso. La única forma de distinguirlo es determinar el punto final local (especialmente el puerto) y extender GetProcessConnection con este punto final local. Desafortunadamente, no hay una manera fácil de hacer esto. Aquí hay una respuesta relacionada con esto: ¿Cómo obtengo el número de puerto local de un HttpWebRequest?
Actualización: si desea monitorear continuamente todas las conexiones para un proceso dado, he escrito una clase de utilidad ProcessTcpConnections que recuerda todas las conexiones y suma su uso. Se usaría así (en un ejemplo de aplicación de consola):
class Program
{
static void Main(string[] args)
{
ProcessTcpConnections p = new ProcessTcpConnections(Process.GetCurrentProcess().Id);
Timer timer = new Timer(UpdateStats, p, 0, 100);
do
{
// let''s 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);
}
}
}