上一篇講了VC++ping命令類比,那個只是個樣本點擊開啟連結
這次我將代碼整合後,做一個實戰的練習,掃描一個IP段,返回所有活動的IP
要完成這個練習,以下有幾個步驟,作為程式員的我們就用代碼說話
首先要做到如何證明一個IP是否在活動,代碼如下
BOOL IpCheck::IsIPActive(char *addr){// 目的IP地址,即要Ping的IP地址 char *szDestIp = addr; // 127.0.0.1 // 建立原始套節字 WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); SOCKET sRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); // 設定接收逾時 SetTimeout(sRaw, 100, TRUE); // 設定目的地址 SOCKADDR_IN dest; dest.sin_family = AF_INET; dest.sin_port = htons(0); dest.sin_addr.S_un.S_addr = inet_addr(szDestIp); // 建立ICMP封包 char buff[sizeof(ICMP_HDR) + 32]; ICMP_HDR* pIcmp = (ICMP_HDR*)buff; // 填寫ICMP封包資料,請求一個ICMP回顯 pIcmp->icmp_type = 8; pIcmp->icmp_code = 0; pIcmp->icmp_id = (USHORT)GetCurrentProcessId(); pIcmp->icmp_checksum = 0; pIcmp->icmp_sequence = 0; // 填充資料部分,可以為任意 memset(&buff[sizeof(ICMP_HDR)], 'E', 32); // 開始發送和接收ICMP封包 USHORT nSeq = 0; char recvBuf[1024]; SOCKADDR_IN from; int nLen = sizeof(from); static int nCount = 0; int nRet; pIcmp->icmp_checksum = 0; pIcmp->icmp_timestamp = GetTickCount(); pIcmp->icmp_sequence = nSeq++; pIcmp->icmp_checksum = checksum((USHORT*)buff, sizeof(ICMP_HDR) + 32); nRet = sendto(sRaw, buff, sizeof(ICMP_HDR) + 32, 0, (SOCKADDR *)&dest, sizeof(dest)); if(nRet == SOCKET_ERROR) { printf(" sendto() failed: %d \n", ::WSAGetLastError()); return -1; } nRet = recvfrom(sRaw, recvBuf, 1024, 0, (sockaddr*)&from, &nLen); if(nRet == SOCKET_ERROR) { if(WSAGetLastError() == WSAETIMEDOUT) { printf("%s timed out\n", addr); return FALSE; }printf("recvfrom() failed: %d\n", WSAGetLastError()); return FALSE;} // 下面開始解析接收到的ICMP封包 int nTick = ::GetTickCount(); if(nRet < sizeof(IPHeader) + sizeof(ICMP_HDR)) { printf(" Too few bytes from %s \n", inet_ntoa(from.sin_addr)); } // 接收到的資料中包含IP頭,IP頭大小為20個位元組,所以加20得到ICMP頭 // (ICMP_HDR*)(recvBuf + sizeof(IPHeader)); ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(recvBuf + 20); if(pRecvIcmp->icmp_type == ICMP_TYPE_REACH_FAIL) // 回顯 { printf("%s reach fail, type %d recvd \n", addr, pRecvIcmp->icmp_type); return FALSE; } if(pRecvIcmp->icmp_id != GetCurrentProcessId()) { printf(" someone else's packet! \n"); return FALSE; } printf("從 %s 返回 %d 位元組:", addr,nRet); printf(" 資料包序號 = %d. \t", pRecvIcmp->icmp_sequence); printf(" 延時大小: %d ms", nTick - pRecvIcmp->icmp_timestamp); printf(" \n");s_array->Insert(addr);WSACleanup(); closesocket(sRaw);return TRUE;}
這個是掃描一個指定的IP地址,但是我們想要掃描多個IP地址的話,必須對IP地址進行轉化,轉化為整數就可以進行累加操作
unsigned long __fastcall IpCheck::InvertIp(unsigned long NormalIp) { unsigned char b1,b2,b3,b4; b1 = NormalIp & 0x00FF; b2 = (NormalIp >> 8) & 0x00FF;b3 = (NormalIp >> 16) & 0x00FF; b4 = (NormalIp >> 24) & 0x00FF;return (b1 <<24) | (b2 << 16) | (b3 << 8) | b4;} //多地址掃描void IpCheck::MutiScan(char *s_ip, char *e_ip){in_addr ia; unsigned long FirstIp,SecondIp; int Delta; char* Addr; FirstIp = inet_addr(s_ip); //任意的開始地址 SecondIp = inet_addr(e_ip); //任意的結束位址 //轉換成能直接遞增和遞減的地址 FirstIp = InvertIp(FirstIp); SecondIp = InvertIp(SecondIp); Delta = SecondIp - FirstIp;s_array->InitBuf(Delta + 1);for(int i = 0; i <= Delta;i++) { ia.S_un.S_addr = InvertIp(FirstIp++); Addr = inet_ntoa(ia);//掃描 IsIPActive(Addr);}}
當我們可以掃描IP後,如何記錄和列印所有活動的IP呢?一般人可能都想到用STL,我個人以前用STL有點陰影,所以自己寫了一個字串數組儲存資料結構。自己寫的心裡有數,靈活性高,可以隨時寫些滿足自己需求的代碼,我就貼出這個儲存類
StringArray.h
#pragma once//這個是自己寫的一個char型指標的字串儲存資料結構,使用的是數組,沒有用STL是因為還沒習慣使用STL,曾經給我帶來陰影//自己寫的靈活性大,方便自己使用的class StringArray{public:StringArray(void);StringArray(int length);~StringArray(void);void InitBuf(int length);int Insert(char *str);char *GetString(int index);char **WrapBuffer();int GetLength();void Release();private:char **strbuf;int length;int count;};
StringArray.cpp
#include "StdAfx.h"#include "StringArray.h"StringArray::StringArray(void){length = 0;strbuf = NULL;count = 0;}StringArray::StringArray(int length){this->length = length;strbuf = (char **)malloc(sizeof(char *) * length);count = 0;}StringArray::~StringArray(void){Release();}void StringArray::InitBuf(int length){this->length = length;strbuf = (char **)malloc(sizeof(char *) * length);for(int i = 0;i < length;i++)strbuf[i] = NULL;}int StringArray::Insert(char *str){if(str == NULL || count == length)return DEFAULT_ERROR;strbuf[count] = (char *)malloc(sizeof(char) * (strlen(str) + 1));if(strbuf[count] == NULL)return DEFAULT_ERROR;memset(strbuf[count], 0, strlen(str) + 1);memcpy(strbuf[count], str, strlen(str));strbuf[count][strlen(str)] = '\0';count++;return DEFAULT_SUCCESS;}char *StringArray::GetString(int index){if(index > length || index < -1)return NULL;return strbuf[index];}int StringArray::GetLength(){return length;}char **StringArray::WrapBuffer(){return strbuf;}void StringArray::Release(){if(strbuf != NULL){for(int i = 0;i < length;i++){if(strbuf[i] != NULL){free(strbuf[i]);strbuf[i] = NULL;}}free(strbuf);strbuf = NULL;}}
有了這些我們就可以實現主函數了
// SCanIP.cpp : 定義控制台應用程式的進入點。//#include "stdafx.h"int _tmain(int argc, _TCHAR* argv[]){if(argc > 3)printf("Error parameter:argc > 3\n");char *param1 = NULL;char *param2 = NULL;IpCheck si;if(argv[1] != NULL && argv[2] != NULL){param1 = w2c(argv[1]);param2 = w2c(argv[2]);si.MutiScan(param1, param2);}else if(argv[1] != NULL){param1 = w2c(argv[1]);si.MutiScan(param1, param1);}else{printf("Error parameter\n");return 0;}char **rs = si.GetIps();int count = 0;printf("存在的IP為:\n");for(int i = 0;i < si.IpCounts();i++){if(rs[i] == NULL)break;printf("%s\t\t", rs[i]);if(count == 3){count = 0;printf("\n");}count++;}if(param1 !=NULL)free(param1);if(param2 !=NULL)free(param2);//system("pause");return 0;}
過程是掃描指定的IP段,最後顯示出所有存在的IP
比如:在CMD下輸入SCanIP.exe 10.72.0.0 10.72.0.255
在一段時間內會掃描某個網段
效果如
以上就是掃描一個IP段的過程