Лучший и быстрый способ узнать, доступен ли IP-адрес - PullRequest
1 голос
/ 24 декабря 2009

Мне нужен самый быстрый способ узнать, доступен ли IP-адрес. На каждом IP-адресе есть сервер, прослушивающий определенный порт, поэтому дайте мне знать, если ваш метод собирается определить, прослушивает ли сервер порт.

Причина этого в том, что предположим, что у меня есть 10 IP-адресов с 10 серверами, прослушивающими порт 101 на каждом IP-адресе. Я хочу, чтобы мой клиент мог найти доступный IP-адрес и подключиться к нему так быстро, как он может (я не хочу, чтобы он ждал 30 секунд, чтобы выяснить, доступен ли IP-адрес, а затем попытался использовать следующий IP-адрес в список)

Может быть, это должно быть сделано в шагах одновременно.

Ответы [ 5 ]

3 голосов
/ 24 декабря 2009

Вы можете использовать потоки, но это приведет к ненужным накладным расходам для этой задачи.

Используйте здесь неблокирующие сокеты (и избегайте неблокирующих сокетов, где вы можете! Действительно, но в этом случае они имеют смысл):

// initiate tcp connects...
for( each of your target host+port pairs ) {
    int socket = socket( AF_INET, SOCK_STREAM );
    ...
#ifdef WIN32
    unsigned long mode = 1;
    ioctlsocket( socket, FIONBIO, &mode );
#else
    int value = fcntl( socket, F_GETFL, 0 );
    fcntl( socket, F_SETFL, value | O_NONBLOCK );
#endif
    ...
    int code = connect( s, target, ... );
    if( code == 0 ) { /* ok, this one did connect, DONE */ }
    // now remember the socket in a list ...
}
// now find the first socket that was successfully connected
while( still not found ) {
    struct timeval tval;
    memset( &tval, 0, sizeof(tval) );
    fd_set write_set, error_set;
    int largest_socket = 0;
    // add sockets to write and error set, and determine largest socket no.
    for( each socket that did not fail until now ) {
        FD_SET( socket, write_set );
        FD_SET( socket, error_set );
        if( socket > largest_socket ) largest_socket = socket;
    }
    // now use select to wait until something happens on the sockets
    int code = select( largest_socket+1, NULL, &write_set, &error_set, &tval );
    if( code < 0 ) { something went terribly wrong }
    else {
        for( each socket that did not fail until now ) {
            if( FD_ISSET( socket, write_set ) ) { you've found it! DONE }
            if( FD_ISSET( socket, error_set ) ) { error, remove this socket from list (for next iteration) }
        }
    }
}

Проверьте документацию для connect и select для более подробной информации!

3 голосов
/ 24 декабря 2009

Хотя вы можете быстро определить, что IP-адрес доступен, ваша проблема заключается в том, что IP-адрес недоступен. Причина в том, что вы не всегда можете окончательно определить, что IP недоступен. Несмотря на то, что существуют некоторые условия, при которых вам будет дано утвердительное уведомление о том, что IP-адрес недоступен, обычно ваш код просто не услышит ответ, и после некоторого времени ожидания ваш код будет считать, что IP-адрес недоступен.

Проблема в определении тайм-аута заключается в топологии сети. Если у вас большая топология (например, Интернет), вам потребуется большой тайм-аут, чтобы справиться с потенциально большими задержками, если вы попытаетесь подключиться к IP, который находится «далеко».

Из вашего описания лучше всего попытаться подключиться ко всем серверам одновременно и использовать первый, который принимает подключение. Вы можете использовать потоки или неблокирующие сокеты. При неблокирующем соединении вызов соединения немедленно возвращается, и затем вы используете выберите , чтобы эффективно определить, когда соединение было завершено (успешно или с ошибкой).

2 голосов
/ 24 декабря 2009

Обычно случайной попытки соединения с коротким временем ожидания достаточно.

Достижимость не очень важна, факт, что существует маршрут от вас к серверу, не имеет значения, можете ли вы подключиться к указанному серверу. Обычно ваш собственный код будет работать так же быстро, как и любой другой метод достижимости, который вы можете придумать.

Если у вас есть проблемы с тем, что он занимает слишком много времени, попробуйте отрегулировать длину ответа или сократить время ожидания.

Простой алгоритм:

shuffle IP addresses
foreach IP in addresses
    attempt connect
    if succeed then
       break
0 голосов
/ 24 декабря 2009

Ниже приведен код, который вы можете использовать для одновременного создания исходящих соединений.

Перебирайте свои IP-адреса и SpawnOutgoing соединения в цикле.
Каждое соединение conn_t* публикуется как LParam в сообщении окна - одновременно.

Вы должны следить за сообщениями и сохранять где-то только первое соединение - игнорировать (удалять) другие соединения.

#define MSG_NEW_CONNECTION  (WM_USER + 1)

struct conn_t {
    SOCKET       s;
    sockaddr_in  server;
};

static
UINT OutgoingWorker(LPVOID param)
{
  // `param` holds "conn_t*"
  assert(param);
  if (!param) return 0;
  conn_t* c = (conn_t*)param;
  if (SOCKET_ERROR == connect(c->s, (SOCKADDR*)&c->server, sizeof c->server)) {
    closesocket(c->s);
    return 0;
  }
  PostMessage(mainwnd, MSG_NEW_CONNECTION, 0, (LPARAM)c); // <-- mainwnd handle
  return 0;
}

conn_t*
SpawnOutgoing(const char* dest_ip, const USHORT dest_port)
{
  if (!dest_ip) return NULL;
  SOCKET  s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (INVALID_SOCKET == s) {
    return NULL;
  }
  conn_t* c = new conn_t;
  // Create the socket here but connect it in the worker
  memset(&c->server, 0, sizeof sockaddr_in);
  c->server.sin_family = AF_INET;
  c->server.sin_addr.s_addr = inet_addr(dest_ip);
  c->server.sin_port = htons(dest_port);
  c->s = s;

  CreateThread(0, 0, OutgoingWorker, c);
  return c;
}
0 голосов
/ 24 декабря 2009

Попробуйте открыть сокет с помощью функции connect () из библиотеки BSD socket . Это так быстро, как вы можете получить, если порт не открыт, он не будет отвечать на пакет SYN.

Ключевой проблемой, как вы понимаете, является связывание потока, который должен ждать SYN-ACK, прежде чем он сможет сделать что-то еще. К счастью, вам не нужны потоки для параллельного ввода / вывода ; однако программирование асинхронных операций может быть тонким; поэтому я бы порекомендовал библиотеку libevent для параллельной отправки операций подключения TCP / IP ... поскольку ядро ​​ выполняет тяжелую работу , вам нужен только один поток это вкл. Вероятно, вы можете выполнить 100 или тысячи подключений в секунду, используя libevent - в зависимости от вашего сетевого оборудования.

Другой альтернативой является Boost :: ASIO , который является более сложным. Но поскольку вы используете C ++, он может подойти вам лучше.

...