Detectando se a conexão com a Internet está ocupada
-
29-10-2019 - |
Pergunta
Estamos desenvolvendo um aplicativo que será instalado no PC e fará upload e download em segundo plano de / para nosso servidor. Um dos requisitos é detectar se a conexão com a Internet está ocupada no momento (digamos, acima de 50% de utilização) e, se estiver, deve desligar e tentar em outra hora. O principal motivo é garantir que o aplicativo não interfira na experiência do usuário se ele estiver jogando, assistindo a um filme online ou baixando arquivos agressivamente
Depois de muito pensar e pesquisar no google e, claro, no SO, ainda não encontrei uma boa maneira de implementar isso, então decidi jogar isso fora aqui. O aplicativo é implementado em C #, .NET 4.0 e estou procurando todas as formas de respostas - seja implementação em C # ou outras linguagens, pseudo-lógica ou abordagem de como alcançar - medição da utilização do tráfego da Internet em PC local com precisão boa o suficiente.
Para evitar a duplicação de esforços, até agora tentei (e porque eles não são adequados)
- Use o WMI para obter estatísticas de rede. A maioria dos postos de SO e soluções existentes, desde então, para se referir a isso como a abordagem, mas não atende ao nosso requisito, pois medir os bytes enviados / recebidos em relação à capacidade da interface de rede (por exemplo, placa Ethernet de 1GB) para utilização resultará em uma boa medida para o tráfego de LAN mas não para o tráfego da Internet (onde a largura de banda real da Internet pode ser apenas 8 Mbps)
- Uso de estatísticas de informações de rede .NET ou contador de desempenho - produzem leituras semelhantes às acima, portanto, têm as mesmas deficiências
- Use ICMP (Ping) e meça o RTT. Foi sugerido que o RTT de 400ms é considerado lento e uma boa indicação para rede ocupada, no entanto, fui informado que o usuário com modem (sim, temos que oferecer suporte a isso), o uso de proxy reverso ou link de micro-ondas geralmente obtém ping acima disso, portanto, não é medir
- Comece a baixar um arquivo conhecido e meça a velocidade - isso por si só gera tráfego que estamos tentando evitar, também se essa verificação for feita com frequência suficiente, nosso aplicativo vai acabar criando muito tráfego de internet - o que novamente não é o ideal
- MOD: Usando BITS - este serviço pode ser desabilitado no pc do usuário, requer mudanças na política de grupo e assume que o servidor é IIS (com configuração personalizada) e em nosso caso nosso servidor não é IIS
Então aqui está, estou confuso e procurando alguns conselhos. Eu destaquei o texto da pergunta para que vocês não se percam lendo isso e se perguntando qual é a pergunta. Obrigado.
Solução
Você pode usar o UPnP para consultar o roteador e recuperar o número de bytes enviados e recebidos pela rede.Você pode continuar verificando esse valor no roteador para determinar qual é a atividade.Infelizmente, essa funcionalidade não parece estar bem documentada, mas é possível implementar a funcionalidade de comunicação UPnP em um aplicativo C #.Você precisará usar o UDP para consultar o roteador (descoberta UPnP) e, depois de encontrar o dispositivo, consulte sua funcionalidade e, em seguida, consulte o número de pacotes enviados e recebidos pelo Dispositivo de gateway da Internet usando um WebClient (TCP).
Code for a UPnP library:
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Xml;
using System.IO;
namespace UPNPLib
{
public class RouterElement
{
public RouterElement()
{
}
public override string ToString()
{
return Name;
}
public List children = new List();
public RouterElement parent;
public string Name;
public string Value;
public RouterElement this[string name] {
get
{
foreach (RouterElement et in children)
{
if (et.Name.ToLower().Contains(name.ToLower()))
{
return et;
}
}
foreach (RouterElement et in children)
{
Console.WriteLine(et.Name);
}
throw new KeyNotFoundException("Unable to find the specified entry");
}
}
public RouterElement(XmlNode node, RouterElement _parent)
{
Name = node.Name;
if (node.ChildNodes.Count
/// Gets the root URL of the device
///
///
public static string GetRootUrl()
{
StringBuilder mbuilder = new StringBuilder();
mbuilder.Append("M-SEARCH * HTTP/1.1\r\n");
mbuilder.Append("HOST: 239.255.255.250:1900\r\n");
mbuilder.Append("ST:upnp:rootdevice\r\n");
mbuilder.Append("MAN:\"ssdp:discover\"\r\n");
mbuilder.Append("MX:3\r\n\r\n");
UdpClient mclient = new UdpClient();
byte[] dgram = Encoding.ASCII.GetBytes(mbuilder.ToString());
mclient.Send(dgram,dgram.Length,new IPEndPoint(IPAddress.Broadcast,1900));
IPEndPoint mpoint = new IPEndPoint(IPAddress.Any, 0);
rootsearch:
dgram = mclient.Receive(ref mpoint);
string mret = Encoding.ASCII.GetString(dgram);
string orig = mret;
mret = mret.ToLower();
string url = orig.Substring(mret.IndexOf("location:") + "location:".Length, mret.IndexOf("\r", mret.IndexOf("location:")) - (mret.IndexOf("location:") + "location:".Length));
WebClient wclient = new WebClient();
try
{
Console.WriteLine("POLL:" + url);
string reply = wclient.DownloadString(url);
if (!reply.ToLower().Contains("router"))
{
goto rootsearch;
}
}
catch (Exception)
{
goto rootsearch;
}
return url;
}
public static RouterElement enumRouterFunctions(string url)
{
XmlReader mreader = XmlReader.Create(url);
XmlDocument md = new XmlDocument();
md.Load(mreader);
XmlNodeList rootnodes = md.GetElementsByTagName("serviceList");
RouterElement elem = new RouterElement();
foreach (XmlNode et in rootnodes)
{
RouterElement el = new RouterElement(et, null);
elem.children.Add(el);
}
return elem;
}
public static RouterElement getRouterInformation(string url)
{
XmlReader mreader = XmlReader.Create(url);
XmlDocument md = new XmlDocument();
md.Load(mreader);
XmlNodeList rootnodes = md.GetElementsByTagName("device");
return new RouterElement(rootnodes[0], null);
}
}
public class RouterMethod
{
string url;
public string MethodName;
string parentname;
string MakeRequest(string URL, byte[] data, string[] headers)
{
Uri mri = new Uri(URL);
TcpClient mclient = new TcpClient();
mclient.Connect(mri.Host, mri.Port);
Stream mstream = mclient.GetStream();
StreamWriter textwriter = new StreamWriter(mstream);
textwriter.Write("POST "+mri.PathAndQuery+" HTTP/1.1\r\n");
textwriter.Write("Connection: Close\r\n");
textwriter.Write("Content-Type: text/xml; charset=\"utf-8\"\r\n");
foreach (string et in headers)
{
textwriter.Write(et + "\r\n");
}
textwriter.Write("Content-Length: " + (data.Length).ToString()+"\r\n");
textwriter.Write("Host: " + mri.Host+":"+mri.Port+"\r\n");
textwriter.Write("\r\n");
textwriter.Flush();
Stream reqstream = mstream;
reqstream.Write(data, 0, data.Length);
reqstream.Flush();
StreamReader reader = new StreamReader(mstream);
while (reader.ReadLine().Length > 2)
{
}
return reader.ReadToEnd();
}
public RouterElement Invoke(string[] args)
{
MemoryStream mstream = new MemoryStream();
StreamWriter mwriter = new StreamWriter(mstream);
//TODO: Implement argument list
string arglist = "";
mwriter.Write("" + "" + "");
mwriter.Write("");//" + arglist + "");
mwriter.Write("");
mwriter.Flush();
List headers = new List();
headers.Add("SOAPAction: \"" + parentschema + "#" + MethodName + "\"");
mstream.Position = 0;
byte[] dgram = new byte[mstream.Length];
mstream.Read(dgram, 0, dgram.Length);
XmlDocument mdoc = new XmlDocument();
string txt = MakeRequest(url, dgram, headers.ToArray());
mdoc.LoadXml(txt);
try
{
RouterElement elem = new RouterElement(mdoc.ChildNodes[0], null);
return elem["Body"].children[0];
}
catch (Exception er)
{
RouterElement elem = new RouterElement(mdoc.ChildNodes[1], null);
return elem["Body"].children[0];
}
}
public List parameters = new List();
string baseurl;
string parentschema;
public RouterMethod(string svcurl, RouterElement element,string pname, string baseURL, string svcpdsc)
{
parentschema = svcpdsc;
baseurl = baseURL;
parentname = pname;
url = svcurl;
MethodName = element["name"].Value;
try
{
foreach (RouterElement et in element["argumentList"].children)
{
parameters.Add(et.children[0].Value);
}
}
catch (KeyNotFoundException)
{
}
}
}
public class RouterService
{
string url;
public string ServiceName;
public List methods = new List();
public RouterMethod GetMethodByNonCaseSensitiveName(string name)
{
foreach (RouterMethod et in methods)
{
if (et.MethodName.ToLower() == name.ToLower())
{
return et;
}
}
throw new KeyNotFoundException();
}
public RouterService(RouterElement element, string baseurl)
{
ServiceName = element["serviceId"].Value;
url = element["controlURL"].Value;
WebClient mclient = new WebClient();
string turtle = element["SCPDURL"].Value;
if (!turtle.ToLower().Contains("http"))
{
turtle = baseurl + turtle;
}
Console.WriteLine("service URL " + turtle);
string axml = mclient.DownloadString(turtle);
XmlDocument mdoc = new XmlDocument();
if (!url.ToLower().Contains("http"))
{
url = baseurl + url;
}
mdoc.LoadXml(axml);
XmlNode mainnode = mdoc.GetElementsByTagName("actionList")[0];
RouterElement actions = new RouterElement(mainnode, null);
foreach (RouterElement et in actions.children)
{
RouterMethod method = new RouterMethod(url, et,ServiceName,baseurl,element["serviceType"].Value);
methods.Add(method);
}
}
}
}
Code for a bandwidth meter:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using UPNPLib;
using System.IO;
namespace bandwidthmeter
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
BinaryReader mreader = new BinaryReader(File.Open("bandwidthlog.txt", FileMode.OpenOrCreate));
if (mreader.BaseStream.Length > 0)
{
prevsent = mreader.ReadInt64();
prevrecv = mreader.ReadInt64();
}
mreader.Close();
List services = new List();
string fullurl = UPNP.GetRootUrl();
RouterElement router = UPNP.enumRouterFunctions(fullurl);
Console.WriteLine("Router feature enumeration complete");
foreach (RouterElement et in router.children)
{
services.Add(new RouterService(et.children[0], fullurl.Substring(0, fullurl.IndexOf("/", "http://".Length+1))));
}
getReceiveDelegate = services[1].GetMethodByNonCaseSensitiveName("GetTotalBytesReceived");
getSentDelegate = services[1].GetMethodByNonCaseSensitiveName("GetTotalBytesSent");
Console.WriteLine("Invoking " + getReceiveDelegate.MethodName);
//Console.WriteLine(services[1].GetMethodByNonCaseSensitiveName("GetTotalPacketsSent").Invoke(null));
Timer mymer = new Timer();
mymer.Tick += new EventHandler(mymer_Tick);
mymer.Interval = 1000;
mymer.Start();
FormClosed += new FormClosedEventHandler(Form1_FormClosed);
}
long prevsent = 0;
long prevrecv = 0;
void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
BinaryWriter mwriter = new BinaryWriter(File.Open("bandwidthlog.txt", FileMode.OpenOrCreate));
mwriter.Write(getsent());
mwriter.Write(getreceived());
mwriter.Flush();
mwriter.Close();
}
long getsent()
{
long retval = Convert.ToInt64(getSentDelegate.Invoke(null).children[0].Value);
if (prevsent > retval)
{
retval = prevsent + retval;
}
return retval;
}
long getreceived()
{
long retval = Convert.ToInt64(getReceiveDelegate.Invoke(null).children[0].Value);
if (prevrecv > retval)
{
retval = prevrecv + retval;
}
return retval;
}
void mymer_Tick(object sender, EventArgs e)
{
label1.Text = "Sent: "+(getsent()/1024/1024).ToString()+"MB\nReceived: "+(getreceived()/1024/1024).ToString()+"MB";
}
RouterMethod getSentDelegate;
RouterMethod getReceiveDelegate;
}
}
Outras dicas
Você já pensou em usar Serviço de transferência inteligente de plano de fundo (BITS) .Ele já foi projetado para fazer este trabalho:
O Background Intelligent Transfer Service (BITS) transfere arquivos (downloads ou uploads) entre um cliente e um servidor e fornece informações sobre o andamento das transferências.Você também pode baixar arquivos de um colega.
e
Preserve a capacidade de resposta de outros aplicativos de rede.
Não tenho certeza se há uma interface gerenciada para ele (posso ver a referência aos cmdlets Powershell), então você pode ter que usar interoperabilidade COM para usá-lo.
Presumindo que você está direcionando PCs com Windows (como você disse que estava desenvolvendo em C #), você olhou BITS , o serviço de transferência inteligente de plano de fundo?
Existem exemplos de como se conectar usando C # no MSDN e em outros lugares, por exemplo, http://msdn.microsoft.com/en-us/magazine/cc188766.aspx