Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.93/15: Рейтинг темы: голосов - 15, средняя оценка - 4.93
3 / 3 / 0
Регистрация: 20.01.2014
Сообщений: 69
1

Сканирование компьютеров на присутствие в сети онлайн. #threads #c++11 #ping #icmp

30.01.2015, 17:32. Показов 2818. Ответов 5
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Пишу программу, которая будет проверять наличие компьютеров в сети. Нашел на msdn код для отправки ICMP запроса. Пытаюсь использовать этот метод для определения компьютера в сети. При последовательном выполнении вроде работает правильно. Если icmp_echo вернула 0, я считаю, что компьютер в сети. Вот код:

Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include <iostream>
#include <string>
 
#include <winsock2.h>
#include <iphlpapi.h>
#include <icmpapi.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")
 
int icmp_echo(std::string ip_add);
 
int computers[10];
 
int main()  {
    
    while(1) {
        /*for(int i=0; i<10; i++)
            computers[i]=icmp_echo("227.0.0.1");*/
        computers[0]=icmp_echo("192.168.1.1");
        computers[1]=icmp_echo("192.168.1.2");
        computers[2]=icmp_echo("192.168.1.3");
        computers[3]=icmp_echo("192.168.1.4");
        computers[4]=icmp_echo("192.168.1.5");
        computers[5]=icmp_echo("192.168.1.6");
        computers[6]=icmp_echo("192.168.1.7");
        computers[7]=icmp_echo("192.168.1.8");
        computers[8]=icmp_echo("192.168.1.9");
        computers[9]=icmp_echo("192.168.1.10");
        
        for(int i=0; i<10; i++)
            printf("%d ",computers[i]);
        printf("\n");
    }
 
    return 0;
}    
 
 
int icmp_echo(std::string ip_add) {
    HANDLE hIcmpFile;
    unsigned long ipaddr = INADDR_NONE;
    DWORD dwRetVal = 0;
    char SendData[] = "ICMP Echo";
    LPVOID ReplyBuffer = NULL;
    DWORD ReplySize = 0;
    
    ipaddr = inet_addr(ip_add.c_str());
    if (ipaddr == INADDR_NONE) {
        //printf("Wrong IP address: %s\n", ip_add.c_str());
        return 1;
    }
 
    hIcmpFile = IcmpCreateFile();
    if (hIcmpFile == INVALID_HANDLE_VALUE) {
        //printf("\tUnable to open handle.\n");
        //printf("IcmpCreatefile returned error: %ld\n", GetLastError() );
        return 2;
    }    
 
    ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData);
    ReplyBuffer = (VOID*) malloc(ReplySize);
    if (ReplyBuffer == NULL) {
        //printf("\tUnable to allocate memory\n");
        return 3;
    }    
 
    dwRetVal = IcmpSendEcho(hIcmpFile, ipaddr, SendData, sizeof(SendData), 
        NULL, ReplyBuffer, ReplySize, 1000);
    if (dwRetVal != 0) {
        PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;
        struct in_addr ReplyAddr;
        ReplyAddr.S_un.S_addr = pEchoReply->Address;
        //printf("\tSent icmp message to %s\n", ip_add.c_str());
        if (dwRetVal > 1) {
            //printf("\tReceived %ld icmp message responses\n", dwRetVal);
            //printf("\tInformation from the first response:\n"); 
        } else {    
            //printf("\tReceived %ld icmp message response\n", dwRetVal);
            //printf("\tInformation from this response:\n"); 
        }
        //printf("\t  Received from %s\n", inet_ntoa( ReplyAddr ) );
        //printf("\t  Status = %ld\n", pEchoReply->Status);
        //printf("\t  Roundtrip time = %ld milliseconds\n", pEchoReply->RoundTripTime);
    } else {
        //printf("\tCall to IcmpSendEcho failed.\n");
        //printf("\tIcmpSendEcho returned error: %ld\n", GetLastError() );
        return 4;
    }
 
    return 0;
}


Разумеется если в сети несколько устройств(компьютеров и т.д.). То ждать долго не хочется. Я добавил потоки c++11. И результаты стали другими. Теперь функция icmp_echo начинает возвращать рандомные значения то 0, то 4. Вот код:
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <mutex>
 
#include <winsock2.h>
#include <iphlpapi.h>
#include <icmpapi.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")
 
int icmp_echo(std::string ip_add);
void threadPing(int num, std::string ip);
 
std::mutex g_lock;
int computers[10];
 
int main()  {
    
 
    std::vector<std::thread*> PingThreads;
 
    /*for(int i=0; i<10; i++) {
        PingThreads.push_back(new std::thread(threadPing,i,"227.0.0.1"));
    }*/
    PingThreads.push_back(new std::thread(threadPing,0,"192.168.1.1"));
    PingThreads.push_back(new std::thread(threadPing,1,"192.168.1.2"));
    PingThreads.push_back(new std::thread(threadPing,2,"192.168.1.3"));
    PingThreads.push_back(new std::thread(threadPing,3,"192.168.1.4"));
    PingThreads.push_back(new std::thread(threadPing,4,"192.168.1.5"));
    PingThreads.push_back(new std::thread(threadPing,5,"192.168.1.6"));
    PingThreads.push_back(new std::thread(threadPing,6,"192.168.1.7"));
    PingThreads.push_back(new std::thread(threadPing,7,"192.168.1.8"));
    PingThreads.push_back(new std::thread(threadPing,8,"192.168.1.9"));
    PingThreads.push_back(new std::thread(threadPing,9,"192.168.1.10"));
    
 
    for(size_t i=0; i<PingThreads.size(); i++) {
        PingThreads[i]->detach();
    }
 
    while(1) {
        g_lock.lock();
            for(int i=0; i<10; i++)
                printf("%d ",computers[i]);
        g_lock.unlock();
        printf("\n");
    }
 
    return 0;
}    
 
void threadPing(int i, std::string ip)
{
    int ret;
    while(1) {
        ret=icmp_echo(ip);
 
        g_lock.lock();
        computers[i]=ret;
        g_lock.unlock();
    }
}
 
int icmp_echo(std::string ip_add) {
    HANDLE hIcmpFile;
    unsigned long ipaddr = INADDR_NONE;
    DWORD dwRetVal = 0;
    char SendData[] = "ICMP Echo";
    LPVOID ReplyBuffer = NULL;
    DWORD ReplySize = 0;
    
    ipaddr = inet_addr(ip_add.c_str());
    if (ipaddr == INADDR_NONE) {
        //printf("Wrong IP address: %s\n", ip_add.c_str());
        return 1;
    }
 
    hIcmpFile = IcmpCreateFile();
    if (hIcmpFile == INVALID_HANDLE_VALUE) {
        //printf("\tUnable to open handle.\n");
        //printf("IcmpCreatefile returned error: %ld\n", GetLastError() );
        return 2;
    }    
 
    ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData);
    ReplyBuffer = (VOID*) malloc(ReplySize);
    if (ReplyBuffer == NULL) {
        //printf("\tUnable to allocate memory\n");
        return 3;
    }    
 
    dwRetVal = IcmpSendEcho(hIcmpFile, ipaddr, SendData, sizeof(SendData), 
        NULL, ReplyBuffer, ReplySize, 1000);
    if (dwRetVal != 0) {
        PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;
        struct in_addr ReplyAddr;
        ReplyAddr.S_un.S_addr = pEchoReply->Address;
        //printf("\tSent icmp message to %s\n", ip_add.c_str());
        if (dwRetVal > 1) {
            //printf("\tReceived %ld icmp message responses\n", dwRetVal);
            //printf("\tInformation from the first response:\n"); 
        } else {    
            //printf("\tReceived %ld icmp message response\n", dwRetVal);
            //printf("\tInformation from this response:\n"); 
        }
        //printf("\t  Received from %s\n", inet_ntoa( ReplyAddr ) );
        //printf("\t  Status = %ld\n", pEchoReply->Status);
        //printf("\t  Roundtrip time = %ld milliseconds\n", pEchoReply->RoundTripTime);
    } else {
        //printf("\tCall to IcmpSendEcho failed.\n");
        //printf("\tIcmpSendEcho returned error: %ld\n", GetLastError() );
        return 4;
    }
 
    return 0;
}


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

P.S. еще хотелось бы сделать проверку не просто по IP, а по хосту например home-pc, notebook и т.д.

P.S.2. Аналогично я делал потоки CLI, потоки на библиотеках QT и на разных языках программирования. Пробовал также пинговать через стандартную утилиту ping. Проблема всегда одна и та же. При последовательном выполнении - верно, в потоках - рандомные значения.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
30.01.2015, 17:32
Ответы с готовыми решениями:

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

Сканирование компьютеров в сети в отдельном потоке
На форме есть listbox. в котором выведены имена компьютеров в сети. затем кодом: For...

Сканирование компьютеров в одной сети на запущеный процесс
Добрый день. Необходимо просканировать диспетчер задач на 200 машинах и выявить один определенный...

Утилита ping (icmp)
Есть код, но в нем ошибки, не знаю как исправить. Вроде смотрю другие реализации в интернете и не...

5
Эксперт С++
4985 / 3092 / 456
Регистрация: 10.11.2010
Сообщений: 11,169
Записей в блоге: 10
30.01.2015, 18:20 2
Зачем нужен бесконечный цикл в 58-й строке? Это как минимум одна из проблем.

Добавлено через 7 минут
Тут detach() не нужен. Нужен join().

Добавлено через 1 минуту
Да и смысл цикла в 44-й строке мне не понятен..
1
3 / 3 / 0
Регистрация: 20.01.2014
Сообщений: 69
30.01.2015, 19:07  [ТС] 3
Изучил функцию IcmpSendEcho и структуру ICMP_ECHO_REPLY более подробно. Оказалось, что статус возвращается в элемент Status структуры ICMP_ECHO_REPLY и принимает значение 0, в случае удачи (#define IP_SUCCESS 0 // The status was success.), иное в противном случае и делать выводы лучше не по этому. В моем случае в начальной реализации я делал выводы по тому, что возвращает IcmpSendEcho ( (dwRetVal != 0) ) и она периодически возвращает 0, что значит ошибка. При помощи GetLastError() я обнаружил, что она возвращает ошибку 11010 (#define WSA_QOS_ADMISSION_FAILURE 11010 // Error due to lack of resources.). То есть в случае ошибки нельзя однозначно сказать в сети адрес или нет. Зато, если вернулся IP_SUCCESS в Status, то адрес в сети. Переписал в соответствии с новой информацией, получилось так. Теперь работает более правильно(нету никаких рандомных значений в выводе), и код функции получился короче:

Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int icmp_echo(std::string ip_add) {
    HANDLE hIcmpFile = IcmpCreateFile();
    if (hIcmpFile != INVALID_HANDLE_VALUE) {
        char RequestData[] = "ping";
        DWORD ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(RequestData);
        char *ReplyBuffer = new char[ReplySize];
 
        const int addr = inet_addr(ip_add.c_str());
        if (addr != INADDR_NONE) {
            if (IcmpSendEcho(hIcmpFile, addr, RequestData, sizeof(RequestData), 0, ReplyBuffer, ReplySize, 1000) != 0) {
                PICMP_ECHO_REPLY icmpReply = (PICMP_ECHO_REPLY)ReplyBuffer;
 
                return icmpReply->Status == IP_SUCCESS;
            }
        }
 
        delete []ReplyBuffer;
        IcmpCloseHandle(hIcmpFile);
    }
 
    return 0;
}


Теперь функция возвращает 1 только в случае успеха, и 0 во всех остальных случаях(недоступен хост, ошибка в выполнении самой функции и прочие ситуации). Жду мнения и критики насчет такого подхода.

Добавлено через 57 секунд
Цитата Сообщение от castaway Посмотреть сообщение
Тут detach() не нужен. Нужен join().
Мне нужно, чтобы бесконечно выводились статусы(мониторинг), а метод join() блокирует основной поток.

Меня больше интересует нужна ли динамически выделенная переменная в векторе, или её можно уничтожить хоть сразу после созданная потока?.
C++
1
PingThreads.push_back(new std::thread(threadPing,0,"192.168.1.1"));
0
Эксперт С++
4985 / 3092 / 456
Регистрация: 10.11.2010
Сообщений: 11,169
Записей в блоге: 10
30.01.2015, 19:34 4
Лучший ответ Сообщение было отмечено BlackUser как решение

Решение

Цитата Сообщение от BlackUser Посмотреть сообщение
Меня больше интересует нужна ли динамически выделенная переменная в векторе, или её можно уничтожить хоть сразу после созданная потока?
Её можно освободить сразу после detach().
Зачем тебе вообще создавать объекты динамически в данном случае?
1
3 / 3 / 0
Регистрация: 20.01.2014
Сообщений: 69
30.01.2015, 23:09  [ТС] 5
Цитата Сообщение от castaway Посмотреть сообщение
Зачем тебе вообще создавать объекты динамически
выходит, что незачем
C++
1
std::thread(threadPing,0,"192.168.1.1").detach();
0
18842 / 9841 / 2409
Регистрация: 30.01.2014
Сообщений: 17,284
30.01.2015, 23:25 6
Лучший ответ Сообщение было отмечено BlackUser как решение

Решение

Цитата Сообщение от BlackUser Посмотреть сообщение
Мне нужно, чтобы бесконечно выводились статусы(мониторинг), а метод join() блокирует основной поток.
Тем не менее, detach тут не нужен. Потому что поток не имеет корректного способа завершения. Не нужно приучать себя к плохому.
join нужно делать после цикла в main. Тогда работа блокироваться не будет. Но, нужно обеспечить, также, корректное завершение потока и цикла в main (например по Ctrl-C). Цикл в main завершился, далее посылаем завершение потоку, вызываем join. Основной поток блокируется на время, которое требуется для завершения второго, потом выходим из программы. Итого все ресурсы корректно освобождены.
1
30.01.2015, 23:25
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
30.01.2015, 23:25
Помогаю со студенческими работами здесь

Ping с использованием ICMP
Помогите пожалуйста, нужно написать функцию, для проверки доступности хоста (Ping) с использованием...

Образец ICMP-ping
Тут недавно был разговор по поводу PingICMP… Есть мощный образец (да простит меня модератор), в...

Блокировать запросы ICMP ping
Всем привет. Подскажите как можно реализовать блокировку ping запросов по протоколу ICMP. Т.е....

С++. Ping с использованием библиотеки icmp.dll
Задание: Создать приложение, реализующее функции, аналогичные утилите ping с использованием...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
6
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru