1. 计算子网内包含的所有的IP地址
// CalculateSubnet.cpp -- 计算指定子网内包含的所有的IP地址 #include "Winsock2.h" #include <string> #include <ctype.h> #pragma comment(lib, "Ws2_32.lib") /** * bool IsValidIP(char* ip) * 功能:判断指定的IP地址是否有效。 * 1. 字符串中必须包含3个符号"." * 2. 被符号"."分隔的字符串必须小于或等于3 * 3. 被符号"."分隔的字符串必须可以转换成不大于255的整数。 **/ bool IsValidIP(char* ip) { std::string sip = ip; for (int judgeTimes = 1; judgeTimes <= 4; ++judgeTimes){ int pos = (int)sip.find_first_of("."); // 找下一个句点 if(0 == pos){ return false; } std::string subip = sip.substr(0, pos); // 将ip分割为左边有效的地方 sip = sip.substr(pos+1, sip.length() - pos ); // 将ip分割为右边有效的地方 if(subip.length() > 3){ // 长度必须不大于3 return false; } for(int i=0; i< (int)subip.length(); i++){ // 检查是否满足全为数字的条件 if(!isdigit(subip[i])){ return false; } } int a = atoi(subip.c_str()); // 检查数字是否在取值范围内 if(1 == judgeTimes && (a < 1 || a > 255)){ return false; } else if((2 == judgeTimes || 3 == judgeTimes) && a > 255){ return false; } else if(4 == judgeTimes && a > 254){ return false; } } return true; } int main() { const int argc = 3; char * argv[] = {"", "192.168.0.1", "255.255.255.0"}; if(argc != 3){ printf("Usage: CalculateSubnet netaddr netmask\r\nExample: CalculateSubnet 192.168.0.0 255.255.255.0\n"); return 1; } if(!IsValidIP(argv[1])) { printf("%s is not a valid ip.\n", argv[1]); return 1; } if(!IsValidIP(argv[2])) { printf("%s is not a valid ip.\n", argv[2]); return 1; } printf("计算指定子网内包含的所有的IP地址\n"); printf("netaddr: %s\n", argv[1]); printf("netmask: %s\n", argv[2]); unsigned long lnetaddr = ntohl(inet_addr(argv[1])); // IP地址 unsigned long lnetmask = ntohl(inet_addr(argv[2])); // 子网掩码 unsigned long l_first_netaddr = lnetaddr & lnetmask; // 子网地址=IP地址&子网掩码 unsigned long l_broadcast = lnetaddr | ~lnetmask; // 广播地址=IP地址|~子网掩码 // 计算子网中包含有效IP地址的数量 long num = l_broadcast - l_first_netaddr - 1; printf("Number of valid IPs: %d\n\n", num); printf("IPs in subnet: \n=============\n"); for(unsigned long i = l_first_netaddr+1; i < l_broadcast; i++) { in_addr IPAddr; IPAddr.S_un.S_addr = ntohl(i); printf("%s\n", inet_ntoa(IPAddr)); } system("pause"); return 0; } /** 运行结果: 计算指定子网内包含的所有的IP地址 netaddr: 192.168.0.1 netmask: 255.255.255.0 Number of valid IPs: 254 IPs in subnet: ============= 192.168.0.1 192.168.0.2 192.168.0.3 192.168.0.4 192.168.0.5 192.168.0.6 192.168.0.7 192.168.0.8 192.168.0.9 192.168.0.10 192.168.0.11 192.168.0.12 192.168.0.13 192.168.0.14 192.168.0.15 192.168.0.16 192.168.0.17 192.168.0.18 192.168.0.19 192.168.0.20 192.168.0.21 192.168.0.22 192.168.0.23 192.168.0.24 192.168.0.25 192.168.0.26 192.168.0.27 192.168.0.28 192.168.0.29 192.168.0.30 192.168.0.31 192.168.0.32 192.168.0.33 192.168.0.34 192.168.0.35 192.168.0.36 192.168.0.37 192.168.0.38 192.168.0.39 192.168.0.40 192.168.0.41 192.168.0.42 192.168.0.43 192.168.0.44 192.168.0.45 192.168.0.46 192.168.0.47 192.168.0.48 192.168.0.49 192.168.0.50 192.168.0.51 192.168.0.52 192.168.0.53 192.168.0.54 192.168.0.55 192.168.0.56 192.168.0.57 192.168.0.58 192.168.0.59 192.168.0.60 192.168.0.61 192.168.0.62 192.168.0.63 192.168.0.64 192.168.0.65 192.168.0.66 192.168.0.67 192.168.0.68 192.168.0.69 192.168.0.70 192.168.0.71 192.168.0.72 192.168.0.73 192.168.0.74 192.168.0.75 192.168.0.76 192.168.0.77 192.168.0.78 192.168.0.79 192.168.0.80 192.168.0.81 192.168.0.82 192.168.0.83 192.168.0.84 192.168.0.85 192.168.0.86 192.168.0.87 192.168.0.88 192.168.0.89 192.168.0.90 192.168.0.91 192.168.0.92 192.168.0.93 192.168.0.94 192.168.0.95 192.168.0.96 192.168.0.97 192.168.0.98 192.168.0.99 192.168.0.100 192.168.0.101 192.168.0.102 192.168.0.103 192.168.0.104 192.168.0.105 192.168.0.106 192.168.0.107 192.168.0.108 192.168.0.109 192.168.0.110 192.168.0.111 192.168.0.112 192.168.0.113 192.168.0.114 192.168.0.115 192.168.0.116 192.168.0.117 192.168.0.118 192.168.0.119 192.168.0.120 192.168.0.121 192.168.0.122 192.168.0.123 192.168.0.124 192.168.0.125 192.168.0.126 192.168.0.127 192.168.0.128 192.168.0.129 192.168.0.130 192.168.0.131 192.168.0.132 192.168.0.133 192.168.0.134 192.168.0.135 192.168.0.136 192.168.0.137 192.168.0.138 192.168.0.139 192.168.0.140 192.168.0.141 192.168.0.142 192.168.0.143 192.168.0.144 192.168.0.145 192.168.0.146 192.168.0.147 192.168.0.148 192.168.0.149 192.168.0.150 192.168.0.151 192.168.0.152 192.168.0.153 192.168.0.154 192.168.0.155 192.168.0.156 192.168.0.157 192.168.0.158 192.168.0.159 192.168.0.160 192.168.0.161 192.168.0.162 192.168.0.163 192.168.0.164 192.168.0.165 192.168.0.166 192.168.0.167 192.168.0.168 192.168.0.169 192.168.0.170 192.168.0.171 192.168.0.172 192.168.0.173 192.168.0.174 192.168.0.175 192.168.0.176 192.168.0.177 192.168.0.178 192.168.0.179 192.168.0.180 192.168.0.181 192.168.0.182 192.168.0.183 192.168.0.184 192.168.0.185 192.168.0.186 192.168.0.187 192.168.0.188 192.168.0.189 192.168.0.190 192.168.0.191 192.168.0.192 192.168.0.193 192.168.0.194 192.168.0.195 192.168.0.196 192.168.0.197 192.168.0.198 192.168.0.199 192.168.0.200 192.168.0.201 192.168.0.202 192.168.0.203 192.168.0.204 192.168.0.205 192.168.0.206 192.168.0.207 192.168.0.208 192.168.0.209 192.168.0.210 192.168.0.211 192.168.0.212 192.168.0.213 192.168.0.214 192.168.0.215 192.168.0.216 192.168.0.217 192.168.0.218 192.168.0.219 192.168.0.220 192.168.0.221 192.168.0.222 192.168.0.223 192.168.0.224 192.168.0.225 192.168.0.226 192.168.0.227 192.168.0.228 192.168.0.229 192.168.0.230 192.168.0.231 192.168.0.232 192.168.0.233 192.168.0.234 192.168.0.235 192.168.0.236 192.168.0.237 192.168.0.238 192.168.0.239 192.168.0.240 192.168.0.241 192.168.0.242 192.168.0.243 192.168.0.244 192.168.0.245 192.168.0.246 192.168.0.247 192.168.0.248 192.168.0.249 192.168.0.250 192.168.0.251 192.168.0.252 192.168.0.253 192.168.0.254 请按任意键继续. . . **/
2. 实现ping功能扫描子网
// scanAllSubnetIPAddress.cpp -- 实现ping功能扫描子网 #include <string> #include <map> #include <list> #include <winsock2.h> #pragma comment(lib, "Ws2_32.lib") using namespace std; const int ICMP_MIN = 8; // ICMP包的最小长度为8个字节,只包含包头 const int DEF_PACKET_SIZE = 32; // 执行ping操作时指定发送数据包的缺省大小 const int MAX_PACKET = 1024; // 执行ping操作时指定发送数据包的最大大小 const int ICMP_ECHO = 8; // 表示ICMP包为回射请求包 const int ICMP_ECHOREPLY = 0; // 表示ICMP包为回射应答包 // IP数据包头结构 typedef struct iphdr { unsigned int h_len:4; // 包头长度 unsigned int version:4; // IP协议版本 unsigned char tos; // 服务类型(TOS) unsigned short total_len; // 包的总长度 unsigned short ident; // 包的唯一标识 unsigned short frag_and_flags; // 标识 unsigned char ttl; // 生存时间(TTL) unsigned char proto; // 传输协议 (TCP, UDP等) unsigned short checksum; // IP校验和 unsigned int sourceIP; unsigned int destIP; }IpHeader; // 执行ping操作时,定义发送IP数据包中包含的ICMP数据头结构 typedef struct _ihdr { BYTE i_type; // 类型 BYTE i_code; // 编码 USHORT i_cksum; // 检验和 USHORT i_id; // 编号 USHORT i_seq; // 序列号 ULONG timestamp; // 时间戳 }IcmpHeader; // 用于描述要执行ping操作的结构体 struct PingPair{ unsigned long ip; // 执行ping操作的IP地址 LARGE_INTEGER starttime; // ping操作的开始时间 LARGE_INTEGER endtime; // ping操作的结束时间 bool flag; // 表示当前IP地址是否在线 int period; // ping操作的用时 PingPair(): ip(0), flag(false), period(-1){} PingPair(int ipp): ip(ipp), flag(false), period(-1) {} }; // 用于发送ICMP包的线程结构 struct ThreadStruct{ std::map<unsigned long, PingPair*> *ips; // 要执行ping操作的IP地址映射表 SOCKET s; // 执行ping操作所使用的套接字 int timeout; // ping超时时间 DWORD tid; // 线程ID bool *sendCompleted; // 标识是否完成批量ping操作 }; /** * 各函数解释 * fill_icmp_data 填充ICMP请求包。 * checksum 计算ICMP校验和。 * decode_resp 对返回的IP数据包进行解码,定位到ICMP数据 * SendIcmp 使用指定的Socket向指定的单个IP地址发送ICMP请求包 * RecvIcmp 接收一个ICMP回应包 * RecvThreadProc 批量接收ICMP回应包的线程函数 * CreateSocket 创建Socket * DestroySocket 释放Socket * pings 启动多线程对一组地址执行ping操作 * FillSubnet 计算指定子网中包含的所有IP地址列表。 * ScanSubnet 扫描子网中所有的IP地址,返回在线的IP地址。 **/ // 填充ICMP请求包 void fill_icmp_data(char * icmp_data, int datasize) { IcmpHeader *icmp_hdr; char *datapart; // 将缓冲区转换为icmp_hdr结构 icmp_hdr = (IcmpHeader*)icmp_data; // 填充各字段的值 icmp_hdr->i_type = ICMP_ECHO; // 将类型设置为ICMP响应包 icmp_hdr->i_code = 0; // 将编码设置为0 icmp_hdr->i_id = (USHORT)GetCurrentThreadId(); // 将编号设置为当前线程的编号 icmp_hdr->i_cksum = 0; // 将校验和设置为0 icmp_hdr->i_seq = 0; // 将序列号设置为0 datapart = icmp_data + sizeof(IcmpHeader); // 定义到数据部分 memset(datapart,'E', datasize - sizeof(IcmpHeader));// 在数据部分随便填充一些数据 return; } // 创建套接字 SOCKET CreateSocket(DWORD timeout) { WSADATA wsaData; SOCKET sockRaw = NULL; // 初始化 if (WSAStartup(MAKEWORD(2,1),&wsaData) != 0){ return sockRaw; } // 创建原始套接字 sockRaw = WSASocket (AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0,WSA_FLAG_OVERLAPPED); if (sockRaw == INVALID_SOCKET) { return sockRaw;// WSASocket 错误 } // 设置接收超时时间 setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout, sizeof(timeout)); // 设置发送超时时间 setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout, sizeof(timeout)); return sockRaw; } // 释放套接字 void DestroySocket(SOCKET sockRaw) { closesocket(sockRaw); WSACleanup(); } // 计算ICMP包的校验和 USHORT checksum(USHORT *buffer, int size) { unsigned long cksum=0; // 把缓冲区中的数据相加 while(size > 1) { cksum += *buffer++; size -= sizeof(USHORT); } if(size ) { cksum += *(UCHAR*)buffer; } cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >> 16); return (USHORT)(~cksum); } // 使用指定的套接字向指定的单个IP地址发送ICMP请求包 // 参数sockRaw,指定发送ICMP请求包的套接字 // 参数ip,指定发送ICMP请求包的目标地址 bool SendIcmp(SOCKET sockRaw, unsigned long ip) { struct sockaddr_in dest,from; // 保存目标地址和源地址 int datasize; // 指定ICMP数据包的大小 int fromlen = sizeof(from); // 源地址长度 unsigned long addr=0; // 保存主机字节序IP地址 USHORT seq_no = 0; // 指定当前ICMP数据包的序号 int ret = -1; // 保存函数的返回值 // 将ip转换为dest,以便执行ping操作 memset(&dest,0,sizeof(dest)); addr = ntohl(ip); dest.sin_addr.s_addr = addr; dest.sin_family = AF_INET; // 设置ICMP数据包的大小 datasize = DEF_PACKET_SIZE; datasize += sizeof(IcmpHeader); // 填充ICMP数据包 char icmp_data[MAX_PACKET]; memset(icmp_data,0,MAX_PACKET); fill_icmp_data(icmp_data,datasize); // 设置ICMP包头中的校验和、时间戳和序号 ((IcmpHeader*)icmp_data)->i_cksum = 0; ((IcmpHeader*)icmp_data)->timestamp = GetTickCount(); ((IcmpHeader*)icmp_data)->i_seq = seq_no++; ((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data, datasize); // 向dest发送ICMP数据包 int bwrote; bwrote = sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest, sizeof(dest)); // 发送失败,则返回false if (bwrote == SOCKET_ERROR){ if (WSAGetLastError() != WSAETIMEDOUT) { ret = false; // 发送错误 } } if (bwrote < datasize ) { return false; } //发送成功,返回true return true; } // 对返回的IP数据包进行解码,定位到ICMP数据 // 因为ICMP数据包含在IP数据包中 int decode_resp(char *buf, int bytes, struct sockaddr_in *from, DWORD tid) { IpHeader *iphdr; // IP数据包头 IcmpHeader *icmphdr; // ICMP包头 unsigned short iphdrlen; // IP数据包头的长度 iphdr = (IpHeader *)buf; // 从buf中IP数据包头的指针 // 计算IP数据包头的长度 iphdrlen = iphdr->h_len * 4 ; // number of 32-bit words *4 = bytes // 如果指定的缓冲区长度小于IP包头加上最小的ICMP包长度,则说明它包含的ICMP数据不完整,或者不包含ICMP数据 if (bytes < iphdrlen + ICMP_MIN) { return -1; //printf("Too few bytes from %s\n",inet_ntoa(from->sin_addr)); } // 定位到ICMP包头的起始位置 icmphdr = (IcmpHeader*)(buf + iphdrlen); // 如果ICMP包的类型不是回应包,则不处理 if (icmphdr->i_type != ICMP_ECHOREPLY) { return -2; //fprintf(stderr,"non-echo type %d recvd\n",icmphdr->i_type); } // 发送的ICMP包ID和接收到的ICMP包ID应该对应 if (icmphdr->i_id != (USHORT)tid){ //(USHORT)GetCurrentProcessId()) { return -3; //fprintf(stderr,"someone else's packet!\n"); } // 返回发送ICMP包和接收回应包的时间差 int time = GetTickCount() - (icmphdr->timestamp); if(time >= 0) return time; else return -4; // 时间值不对 } // 接收一个ICMP回应包 // 参数sockRaw指定接收ICMP回应包的套接字 // 参数ip指定发送ICMP回应包的IP地址 // 参数tid指定发送ICMP回应包的线程ID int RecvIcmp(SOCKET sockRaw, unsigned long *ip, DWORD tid) { struct sockaddr_in from; // 接收到ICMP的来自的地址 int fromlen = sizeof(from); // 地址from的长度 int bread; // 调用recvfrom()函数的返回结果 char recvbuf[MAX_PACKET]; // 用于接收ICMP回应我的缓冲区 // 接收ICMP回应包 bread = recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr*)&from, &fromlen); if (bread == SOCKET_ERROR){ if (WSAGetLastError() == WSAETIMEDOUT) { return -1; // 超时 } else return -9; // 接收错误 } // 对ICMP回应包进行解析 int time = decode_resp(recvbuf,bread,&from, tid); // 如果可以ping通,则返回发送ICMP请求包到接收ICMP回应包的时间,否则返回-1 if( time >= 0 ){ *ip = ntohl(from.sin_addr.S_un.S_addr); return time; } else{ return -1; } } // 接收ICMP回应包的线程 DWORD WINAPI RecvThreadProc(void *param) { int count = 0; ThreadStruct *unionStruct = (ThreadStruct *)param; // 参数为ThreadStruct,即线程数量 DWORD startTime = GetTickCount(); // 记录开始时间 DWORD timeout = unionStruct->timeout * 2; // 超时时间加长 SOCKET sockRaw = unionStruct->s; // 设置套接字 std::map<unsigned long, PingPair*> *ips = unionStruct->ips; // 设置要执行批量ping操作的IP地址 DWORD tid = unionStruct->tid; // 设置线程ID // 如果批量ping操作未完成,并且没有超时,则调用RecvIcmp()函数接收一个IP地址的响应包 std::map<unsigned long, PingPair*>::iterator itr; while( !*(unionStruct->sendCompleted) || GetTickCount() - startTime < timeout){ unsigned long ip; int ret = RecvIcmp(sockRaw, &ip, tid); // 接收一个IP的ICMP响应包 if(ret < 0){ continue; } if( (itr = ips->find(ip)) != ips->end() && !itr->second->flag){ QueryPerformanceCounter( &(itr->second->endtime) ); // 获取结束时间 itr->second->flag = true; // 设置在线标识 } } return 0; } // ping一组设备 int pings(std::map<unsigned long, PingPair*> &ips, DWORD timeout) { SOCKET s = CreateSocket(timeout); // 创建ping操作使用的套接字 if(s == INVALID_SOCKET){ // 如果创建失败,则返回 return -1; } // 准备执行批量ping操作的T ThreadStruct unionStruct; unionStruct.ips = &ips; // 要执行ping操作的IP地址映射表 unionStruct.s = s; // 发送和接收ICMP数据包的套接字 unionStruct.timeout = timeout; // 超时时间 unionStruct.tid = GetCurrentThreadId(); // 线程ID unionStruct.sendCompleted = new bool(false); // 标识为未发送完成 // 创建批量ping操作线程,线程函数为RecvThreadProc,参数为unionStruct DWORD tid; HANDLE handle = CreateThread(NULL, 0, RecvThreadProc, &unionStruct, 0, &tid); // 依次向ips中所有IP地址发送 std::map<unsigned long, PingPair*>::iterator itr; for(itr = ips.begin();itr != ips.end();itr++){ SendIcmp(s, itr->first); // 发送ICMP请求包 QueryPerformanceCounter( &itr->second->starttime ); // 记录初始时间 Sleep(10); } // 因为ICMP是基于不可靠的UDP协议的 // 为了防止目标IP没有收到ICMP请求包,这里再发送一次 for(itr = ips.begin(); itr != ips.end(); itr++){ SendIcmp(s, itr->first); // 发出所有数据 Sleep(10); } // 将发送完成标识设置为true *(unionStruct.sendCompleted) = true; // 等待接收线程返回 DWORD ret = WaitForSingleObject(handle, timeout * 3); // 结束线程 if(ret == WAIT_TIMEOUT) { printf("Kill Thread\n"); TerminateThread(handle, 0); } CloseHandle(handle); // 关闭线程句柄 DestroySocket(s); // 释放套接字 // 获取CPU每秒钟跑几个ticks LARGE_INTEGER ticksPerSecond; QueryPerformanceFrequency(&ticksPerSecond); // 依次对所有IP地址进行处理 for(itr = ips.begin();itr != ips.end();itr++){ // 如果在线(flag=true),则记录执行ping操作的时间 if(itr->second->flag == true){ double elapsed = ((double)(itr->second->endtime.QuadPart - itr->second->starttime.QuadPart) / ticksPerSecond.QuadPart); // 經過的時間, 依自己程式需求選擇精準度 if(elapsed <= 0) elapsed = 0; itr->second->period = (int)(elapsed*1000); } } delete unionStruct.sendCompleted; return 0; } // 计算子网地址为NetAddr和子网掩码为NetMask的子网中包含的所有IP地址列表 list<string> FillSubnet(string NetAddr, string NetMask) { list<string> IpList; // 将网络地址和子网掩码从网络字节顺序转换为主机字节顺序 unsigned long _inetaddr = ntohl(inet_addr(NetAddr.c_str())); unsigned long _inetmask = ntohl(inet_addr(NetMask.c_str())); // 计算网络地址和广播地址 unsigned long first_netaddr = _inetaddr & _inetmask; unsigned long broadcast = _inetaddr | ~_inetmask; // 计算子网中包含有效IP地址的数量 long num = broadcast - first_netaddr - 1; for(unsigned long i=first_netaddr+1; i<broadcast; i++){ // 保存IP地址的结构体 in_addr IPAddr; IPAddr.S_un.S_addr = ntohl(i); // 添加每个IP地址到IpList中 IpList.push_back(inet_ntoa(IPAddr)); } return IpList; } list<string> ScanSubnet(string NetAddr, string NetMask, DWORD timeOut) { // 计算子网中的IP地址列表 list<string> IpList = FillSubnet(NetAddr, NetMask); // 将IpList转换成用来执行ping操作的所有IP地址ipAll std::map<unsigned long, PingPair*> ipAll; list<string>::iterator IpItr; // 将IpList转换为ipAll,为调用pings()函数准备数据 for(IpItr = IpList.begin(); IpItr != IpList.end(); IpItr++){ string ip = *IpItr; if(ip.empty()) // 如果设备IP地址为空,则不处理 continue; unsigned int uip = ntohl(inet_addr(ip.c_str())); PingPair *p = new PingPair(uip); ipAll[uip] = p; } // 执行批量ping操作 pings(ipAll, timeOut); // 将活动IP地址保存在ActiveIpList中 list<string> ActiveIpList; std::map<unsigned long, PingPair*>::iterator ipItr; for(ipItr=ipAll.begin();ipItr!=ipAll.end();ipItr++){ if(ipItr->second->flag){ in_addr IPAddr; IPAddr.S_un.S_addr = ntohl(ipItr->second->ip); // 将活动IP地址保存在ActiveIpList中 ActiveIpList.push_back(inet_ntoa(IPAddr)); } delete ipItr->second; } return ActiveIpList; } int main() { const int argc = 3; char * argv[] = {"", "192.168.0.1", "255.255.255.0"}; if(argc != 3) { printf("参数数量不正确。请指定要ping的子网的子网地址和子网掩码。\n"); return -1; } // 执行ping操作 printf("ping subnet: %s, %s...\n", argv[1], argv[2]); // 扫描子网 list<string> ActiveIpList = ScanSubnet(argv[1], argv[2], 500); // 显示所有在线IP地址 printf("The Active IP Address is below:\n"); list<string>::iterator IpItr; for(IpItr = ActiveIpList.begin(); IpItr != ActiveIpList.end(); IpItr++){ string ip = *IpItr; if(ip.empty()) // 如果设备IP地址为空,则不处理 continue; printf("%s\n", ip.c_str()); } system("pause"); return 0; }