Вы предложили алгоритм, который вам нужен. Одна проблема заключается в динамической генерации IP-адресов кандидатов.
Обычно возможный диапазон IP-адресов - тот, который задается маской подсети (http://en.wikipedia.org/wiki/Subnetwork). Точнее говоря, часть IP, которая изменяется, это та часть, когда в маске подсети у вас есть 0 бит (всегда в конце маски).
В вашем примере:
- если маска 255.255.255.0, то ваш возможный диапазон IP-адресов
192.168.0. *.
- если IP также может быть 192.168. 1 . * Тогда, вероятно, маска должна быть 255.255. 0 .0
- Вы также можете иметь маску, например 255.255.255.128, и диапазон будет 192.18.1. [1-126]. Вы можете практически узнать больше, используя http://www.subnet -calculator.com /
Единственные другие возможности, которые вызывают вашу проблему , которые, как я вижу, имеют эти различные диапазоны:
- в вашей сети больше DHCP-серверов, что очень плохо, так как у вас будут "условия гонки". Решение здесь состоит в том, чтобы исправить вашу инфраструктуру, удалив все, кроме 1 DHCP-сервера
- вы вручную установили IP-адреса (возможно, на ноутбуках). Решение состоит в том, чтобы перейти на DHCP (если вам нужен определенный IP, который всегда будет назначаться определенному компьютеру, используйте статический DHCP)
Возвращаясь к проблеме обнаружения проблемы проверки того, что «что-то» прослушивает определенный порт, протокол ICMP здесь не самый лучший, поскольку большинство брандмауэров фильтруют как широковещательный ICMP, так и один ICMP. Если мы действительно говорим о сервере, скорее всего, вам пришлось вручную открыть порт, который вы ищете. Кроме того, , даже если все компьютеры отвечают, вы по-прежнему не знаете, если они размещают ваш хотел сервис .
Приведенное ниже решение включает в себя вычисление возможного диапазона возможных IP-адресов . После этого вы перебираете их, чтобы посмотреть, сможете ли вы подключиться к вашему порту .
В этой реализации я тестирую последовательно, что оказывается очень медленным, так как время ожидания соединения составляет 30 секунд, если хост не включен. Для нескольких сотен кандидатов это звучит не слишком хорошо. Однако, если большинство хостов доступно (даже если они не размещают ваш сервис), все будет работать в несколько раз быстрее.
Вы можете улучшить программу, узнав, как уменьшить этот тайм-аут (я не мог узнать, как в отведенное мне время) или использовать пользовательский тайм-аут, как показано в Как настроить время ожидания подключения к сокету . Вы также можете использовать многопоточность и добавление адреса, который работал в поточно-безопасной коллекции, и работать с ним оттуда.
Также, вы могли бы попробовать пинговать (ICMP) раньше, но вы могли пропустить действительные серверы .
static void Main(string[] args)
{
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
int wantedPort = 21; //this is the port you want
byte[] msg = Encoding.ASCII.GetBytes("type msg here");
foreach (NetworkInterface netwIntrf in NetworkInterface.GetAllNetworkInterfaces())
{
Console.WriteLine("Interface name: " + netwIntrf.Name);
Console.WriteLine("Inteface working: {0}", netwIntrf.OperationalStatus == OperationalStatus.Up);
//if the current interface doesn't have an IP, skip it
if (! (netwIntrf.GetIPProperties().GatewayAddresses.Count > 0))
{
break;
}
//Console.WriteLine("IP Address(es):");
//get current IP Address(es)
foreach (UnicastIPAddressInformation uniIpInfo in netwIntrf.GetIPProperties().UnicastAddresses)
{
//get the subnet mask and the IP address as bytes
byte[] subnetMask = uniIpInfo.IPv4Mask.GetAddressBytes();
byte[] ipAddr = uniIpInfo.Address.GetAddressBytes();
// we reverse the byte-array if we are dealing with littl endian.
if (BitConverter.IsLittleEndian)
{
Array.Reverse(subnetMask);
Array.Reverse(ipAddr);
}
//we convert the subnet mask as uint (just for didactic purposes (to check everything is ok now and next - use thecalculator in programmer mode)
uint maskAsInt = BitConverter.ToUInt32(subnetMask, 0);
//Console.WriteLine("\t subnet={0}", Convert.ToString(maskAsInt, 2));
//we convert the ip addres as uint (just for didactic purposes (to check everything is ok now and next - use thecalculator in programmer mode)
uint ipAsInt = BitConverter.ToUInt32(ipAddr, 0);
//Console.WriteLine("\t ip={0}", Convert.ToString(ipAsInt, 2));
//we negate the subnet to determine the maximum number of host possible in this subnet
uint validHostsEndingMax = ~BitConverter.ToUInt32(subnetMask, 0);
//Console.WriteLine("\t !subnet={0}", Convert.ToString(validHostsEndingMax, 2));
//we convert the start of the ip addres as uint (the part that is fixed wrt the subnet mask - from here we calculate each new address by incrementing with 1 and converting to byte[] afterwards
uint validHostsStart = BitConverter.ToUInt32(ipAddr, 0) & BitConverter.ToUInt32(subnetMask, 0);
//Console.WriteLine("\t IP & subnet={0}", Convert.ToString(validHostsStart, 2));
//we increment the startIp to the number of maximum valid hosts in this subnet and for each we check the intended port (refactoring needed)
for (uint i = 1; i <= validHostsEndingMax; i++)
{
uint host = validHostsStart + i;
//byte[] hostAsBytes = BitConverter.GetBytes(host);
byte[] hostBytes = BitConverter.GetBytes(host);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(hostBytes);
}
//this is the candidate IP address in "readable format"
String ipCandidate = Convert.ToString(hostBytes[0]) + "." + Convert.ToString(hostBytes[1]) + "." + Convert.ToString(hostBytes[2]) + "." + Convert.ToString(hostBytes[3]);
Console.WriteLine("Trying: " + ipCandidate);
try
{
//try to connect
sock.Connect(ipCandidate, wantedPort);
if (sock.Connected == true) // if succesful => something is listening on this port
{
Console.WriteLine("\tIt worked at " + ipCandidate);
sock.Close();
sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
//else -. goes to exception
}
catch (SocketException ex)
{
//TODO: if you want, do smth here
Console.WriteLine("\tDIDN'T work at " + ipCandidate);
}
}
}
Console.ReadLine();
}
sock.Close();
}