Há um tempo abri um tema em forma de pergunta no Stackoverflow, tanto em português quanto em inglês.
Em uma rede local, queria ter controle de dispositivos que se conectassem a um servidor para realizar tarefas automáticas. O servidor local poderia enviar e receber comandos desses dispositivos. Além do essencial, queria gravar um histórico, e saber quem era quem, pelo IP. Como os dispositivos tinham muitas limitações (não tinham processadores para instalar ferramentas, apenas placas de WiFi), o único jeito era capturar a comunicação TCP/IP destes dispositivos com o servidor. A exclusividade teórica do MAC-Address de uma placa WiFi poderia ser o identificador exclusivo de cada dispositivo. Mas como conseguir isso em uma plataforma mais universal?
Graças ao Alessandro Schneider, pude desenvolver uma solução bem simples. Ele ainda recomenda o uso para Linux do arping, solução mais robusta, competente.
A seguir disponho uma solução para fazer um sistema que rode em W10 e Linux. Esta solução foi testada em um Raspberry 3.0.
using System.Diagnostics;
namespace Ferramentas
{
public class Rede
{
///FOR LINUX
public static string Bash(string cmd)
{
var escapedArgs = cmd.Replace("\"", "\\\"");
var process = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = "/bin/bash",
Arguments = $"-c \"{escapedArgs}\"",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
}
};
process.Start();
string result = process.StandardOutput.ReadToEnd();
process.WaitForExit();
return result;
}
///FOR WINDOWS
public static string CMD(string cmd)
{
var process = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $@"/c {cmd}",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
}
};
process.Start();
string result = process.StandardOutput.ReadToEnd();
process.WaitForExit();
return result;
}
//Mais métodos
}
}
Poderia transformar esses dois métodos em um só? Sim!!! Mas estão aqui, separados, apenas para mostrar que muitas coisas em Windows e Linux são comuns com o .NET Core.
Continuando…
using System.Diagnostics;
using System.Net.NetworkInformation;
namespace Ferramentas
{
public class Rede
{
//Método Bash
//Método CMD
//Tratativa de erro simplificada, sem Log, sem envio por e-mail e sem _erro.Clear()
private static StringBuilder _erro = new StringBuilder();
public static string ErrorMessage { get { return _erro.ToString(); } }
///Primeira tentativa de comunicação
public static bool Ping(string ipOrName, int timeout = 0, bool throwExceptionOnError = false)
{
bool p = false;
try
{
using (Ping ping = new Ping())
{
PingReply reply = null;
if (timeout > 0)
reply = ping.Send(ipOrName, timeout);
else
reply = ping.Send(ipOrName);
if (reply != null)
p = (reply.Status == IPStatus.Success);
//p = Convert.ToInt32(reply.RoundtripTime);
}
}
catch (PingException e)
{
_erro.Append(e.Message);
_erro.Append(Environment.NewLine);
if (throwExceptionOnError) throw e;
}
catch (Exception ex)
{
_erro.Append(ex.Message);
_erro.Append(Environment.NewLine);
}
return p;
}
//Mais métodos
}
}
Agora usaremos duas classes (poderia ser uma) para chamar as ferramentas. Essas classes é que poderiam ser as públicas, para uso externo.
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Text;
using System.Text.RegularExpressions;
namespace Ferramentas
{
public class Rede
{
//Método Bash
//Método CMD
//Método Ping
public static string TryGetMacAddressOnLinux(string ip)
{
_erro.Clear();
//Descobrir o IP para usar o comando arp (comum tanto em Windows quanto em Linux)
if (!Ping(ip))
_erro.Append("Não foi possível testar a conectividade (ping) com o ip informado.\n");
//O comando arp devolve o MAC-Address do IP informado
string arp = $"arp -a {ip}";
string retorno = Bash(arp);
StringBuilder sb = new StringBuilder();
//A diferença para o Windows é o formato do retorno. Linux usa ":" como separador
string pattern = @"(([a-f0-9]{2}:?){6})";
int i = 0;
foreach (Match m in Regex.Matches(retorno, pattern, RegexOptions.IgnoreCase))
{
if (i > 0)
//Contrariando Windows e Linux, devolvo o resultado com o separador ";".
sb.Append(";");
sb.Append(m.ToString());
i++;
}
return sb.ToString();
}
public static string TryGetMacAddressOnWindows(string ip)
{
_erro.Clear();
//Descobrir o IP para usar o comando arp (comum tanto em Windows quanto em Linux)
if (!Ping(ip))
_erro.Append("Não foi possível testar a conectividade (ping) com o ip informado.\n");
//O comando arp devolve o MAC-Address do IP informado
string arp = $"arp -a {ip}";
string retorno = CMD(arp);
StringBuilder sb = new StringBuilder();
//A diferença para o Linux é o formato do retorno. Windows usa "-" como separador
string pattern = @"(([a-f0-9]{2}-?){6})";
int i = 0;
foreach (Match m in Regex.Matches(retorno, pattern, RegexOptions.IgnoreCase))
{
if (i > 0)
//Contrariando Windows e Linux, devolvo o resultado com o separador ";".
sb.Append(";");
sb.Append(m.ToString());
i++;
}
return sb.ToString();
}
//Mais métodos
}
}
Agora, para usar, pode-se usar alguma lógica que ache mais conveniente.
Em essência seria algo como:
public Socket MeuSocket { get; set; }
IPEndPoint remoteIpEndPoint = MeuSocket.RemoteEndPoint as IPEndPoint;
if (remoteIpEndPoint == null)
{
//Trata erro e sai
}
if (_tipo == MeuEnum.LINUX_ARM || _tipo == MeuEnum.LINUX)
{
macAddress = Rede.TryGetMacAddressOnLinux(remoteIpEndPoint.Address.ToString());
}
else
{
macAddress = Rede.TryGetMacAddressOnWindows(remoteIpEndPoint.Address.ToString());
}
Espero que ajude alguém!!
Deixe um comentário