C++ Exercises(十七)---网际校验和算法

     在《基于ARP协议获取局域网内主机MAC地址》中使用了WinpCap来发送ARP请求,查询局域网内主机MAC地址,这篇来试试直接用Windows API函数来实现,最后再来探索用于IP,TCP,UDP等众多协议的网际校验和算法。

1,查询局域网主机MAC地址

 

  
  
  
  
  1. #include <WinSock2.h>  
  2. #include <IPHlpApi.h>  
  3. #include <iostream>  
  4. using namespace std;  
  5.  
  6. #pragma comment(lib,"Iphlpapi")  
  7. #pragma comment(lib,"Ws2_32")  
  8.  
  9. int _tmain(int argc, _TCHAR* argv[])  
  10. {  
  11.     MIB_IPADDRTABLE* pIPAddrTable = (MIB_IPADDRTABLE*)malloc(sizeof(MIB_IPADDRTABLE));  
  12.     ULONG dwSize=0,dwRetVal=0;  
  13.     if (GetIpAddrTable(pIPAddrTable,&dwSize,0)==ERROR_INSUFFICIENT_BUFFER)  
  14.     {  
  15.         free(pIPAddrTable);  
  16.         pIPAddrTable = (MIB_IPADDRTABLE*)malloc(dwSize);  
  17.     }  
  18.     if ((dwRetVal=GetIpAddrTable(pIPAddrTable,&dwSize,0))==NO_ERROR)  
  19.     {  
  20.         ULONG ulHostIp = ntohl(pIPAddrTable->table[0].dwAddr); //本机IP  
  21.         ULONG ulHostMask = ntohl(pIPAddrTable->table[0].dwMask); //子网掩码  
  22.         for (ULONG i= 1;i<(~ulHostMask);++i)  
  23.         {  
  24.             static ULONG ulNo = 0;  
  25.             HRESULT hr;  
  26.             IPAddr ipAddr;  
  27.             ULONG pulMac[2];  
  28.             ULONG ulLen;  
  29.             ipAddr = htonl(i+(ulHostIp&ulHostMask));  
  30.             memset(pulMac,0xff,sizeof(pulMac));  
  31.             ulLen = 6;  
  32.             hr = SendARP(ipAddr,0,pulMac,&ulLen); //发送ARP请求  
  33.             if (ulLen==6)  
  34.             {  
  35.                 ulNo++;  
  36.                 PBYTE pbHexMax = (PBYTE)pulMac;  
  37.                 unsigned char * strIpAddr = (unsigned char *)(&ipAddr);  
  38.                 printf("%d:MAC地址% 02X:% 02X:% 02X:% 02X:% 02X:% 02X IP地址% d. % d. % d. % d\n",ulNo,pbHexMax[0],pbHexMax[1],pbHexMax[2],pbHexMax[3],pbHexMax[4],pbHexMax[5],strIpAddr[0],strIpAddr[1],strIpAddr[2],strIpAddr[3]);  
  39.             }  
  40.         }  
  41.     }  
  42.     else 
  43.     {  
  44.         printf("失败");  
  45.     }  
  46.     printf("结束!\n");  
  47.     free(pIPAddrTable);  
  48.     return 0;  

2,获取本机网卡信息

 

  
  
  
  
  1. #include <WinSock2.h>  
  2. #include <IPHlpApi.h>  
  3. #include <iostream>  
  4. using namespace std;  
  5.  
  6. #pragma comment(lib,"Iphlpapi")  
  7. #pragma comment(lib,"Ws2_32")  
  8.  
  9. int _tmain(int argc, _TCHAR* argv[])  
  10. {  
  11.     PIP_ADAPTER_INFO pAdapterInfo;  
  12.     PIP_ADAPTER_INFO pAdapter = NULL;  
  13.     DWORD dwRetVal = 0;  
  14.     pAdapterInfo = (IP_ADAPTER_INFO*)malloc(sizeof(IP_ADAPTER_INFO));  
  15.     ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);  
  16.     dwRetVal = GetAdaptersInfo(pAdapterInfo,&ulOutBufLen);  
  17.     if (dwRetVal==ERROR_BUFFER_OVERFLOW)  
  18.     {  
  19.         free(pAdapterInfo);  
  20.         pAdapterInfo = (IP_ADAPTER_INFO*)malloc(ulOutBufLen);  
  21.         dwRetVal = GetAdaptersInfo(pAdapterInfo,&ulOutBufLen);  
  22.     }  
  23.     if (dwRetVal==NO_ERROR)  
  24.     {  
  25.         pAdapter = pAdapterInfo;  
  26.         while (pAdapter)  
  27.         {  
  28.             printf("适配器名称:\t% s\n",pAdapter->AdapterName);  
  29.             printf("适配器描述信息:\t% s\n",pAdapter->Description);  
  30.             printf("MAC地址:\t% 02X:% 02X:% 02X:% 02X:% 02X:% 02X\n",pAdapter->Address[0],pAdapter->Address[1],  
  31.                 pAdapter->Address[2],pAdapter->Address[3],pAdapter->Address[4],pAdapter->Address[5]);  
  32.             printf("IP地址:\t% s\n",pAdapter->IpAddressList.IpAddress.String);  
  33.             printf("子网掩码:\t% s\n",pAdapter->IpAddressList.IpMask.String);  
  34.             printf("网关地址:\t% s\n",pAdapter->GatewayList.IpAddress.String);  
  35.             if (pAdapter->DhcpEnabled)  
  36.             {  
  37.                 printf("DHCP enabled: yes\n");  
  38.                 printf("DHCP服务器:\t% s\n",pAdapter->DhcpServer.IpAddress.String);  
  39.                 printf("租约:% ld\n",pAdapter->LeaseObtained);  
  40.             }  
  41.             else 
  42.             {  
  43.                 printf("DHCP enabled:no\n");  
  44.             }  
  45.             if (pAdapter->HaveWins)  
  46.             {  
  47.                 printf("Have Wins:Yes\n");  
  48.                 printf("Primary Wins Server:\t% s\n",pAdapter->PrimaryWinsServer.IpAddress.String);  
  49.                 printf("Secondary Server:\t% s\n",pAdapter->SecondaryWinsServer.IpAddress.String);  
  50.             }  
  51.             else 
  52.             {  
  53.                 printf("Have Wins:No\n");  
  54.             }  
  55.             pAdapter = pAdapter->Next;  
  56.         }  
  57.     }  
  58.     else 
  59.     {  
  60.         printf("失败\n");  
  61.     }  
  62.     return 0;  

3,网际校验和(internet checksum)算法

      IPTCP,UDP等许多协议的头部都设置了校验和项,计算校验和的算法一般采用网际校验和算法,它将被校验的数据按16位进行划分(若数据字节长度为奇数,则在数据尾部补一个字节0),对每16位求反码和,然后再对和取反码。

  
  
  
  
  1. #include<iostream>  
  2. #include<fstream>  
  3. using namespace std;  
  4. #include<winsock.h>            // 本机字节序转换为网络字节序:htons  
  5. #pragma comment(lib, "WS2_32.LIB")  
  6.  
  7. /**************************************************************************  
  8.  * 计算给定数据的校验和  
  9.  *  
  10.  *        输入参数:  
  11.  *            pBuffer        指向需要校验的数据缓冲区  
  12.  *            nSize        需要校验的数据的大小,以字节为单位  
  13.  *  
  14.  *        返回值:  
  15.  *            16位的校验结果  
  16.  *  
  17.  **************************************************************************/ 
  18. unsigned short checksum_calculating(unsigned short *pBuffer, int nSize)  
  19. {  
  20.     unsigned long dwCksum = 0;        // 32位累加和  
  21.     // 以两字节为单位反复累加  
  22.     while(nSize > 1)  
  23.     {  
  24.         dwCksum += *pBuffer++;  
  25.         nSize -= sizeof(unsigned short);  
  26.     }  
  27.     // 如果总字节数为奇数则加上最后一个字节  
  28.     if (nSize)  
  29.     {  
  30.         dwCksum += *(unsigned char*) pBuffer;  
  31.     }  
  32.     // 将位累加和的高位与低位第一次相加  
  33.     dwCksum = (dwCksum >> 16) + (dwCksum & 0xffff);  
  34.     // 将上一步可能产生的高位进位再次与低位累加  
  35.     dwCksum += (dwCksum >> 16);  
  36.     // 返回位校验和  
  37.     return (unsigned short) (~dwCksum);  
  38. }  
  39.  
  40.  
  41. int main(int argc, char * argv[])  
  42. {  
  43.           
  44.     // 创建输入文件流  
  45.     ifstream fInfile;  
  46.     fstream fOutfile; // 创建输出文件流  
  47.     fInfile.open(argv[1], ios::in|ios::binary); // 以二进制方式打开指定的输入文件  
  48.     fInfile.seekg(0, ios::end); // 把文件指针移到文件末尾  
  49.     unsigned short wLen = (unsigned short)fInfile.tellg();// 取得输入文件的长度  
  50.     fInfile.seekg(0, ios::beg); // 文件指针位置初始化  
  51.     // 定义数据报缓冲区,缓冲区大小为+wLen ,其中为数据报类型字段、长度字段  
  52.     // 以及校验和字段的长度和,wLen为数据字段长度,即输入文件长度(以字节为单位)  
  53.     char * pBuf = new char[4 + wLen];  
  54.     pBuf[0] = unsigned char(0xab);        // 给数据报类型字段赋值,这里随便弄了个0Xab  
  55.     pBuf[1] = unsigned char(wLen);        // 给数据报长度字段赋值  
  56.     *(unsigned short *)(pBuf + 2) = 0;    // 计算校验和之前,校验和字段先置为0  
  57.     fInfile.read(pBuf+4, wLen);            // 根据输入文件填充数据报的数据字段  
  58.     // 计算校验和并把结果填入到数据报的校验和字段  
  59.     *(unsigned short *)(pBuf+2) = checksum_calculating((unsigned short *)pBuf,4+wLen);  
  60.     // 输出校验和计算结果  
  61.     cout.width(4);                      
  62.     cout << "校验和为:x" << hex << htons( *(unsigned short *)(pBuf+2) )  
  63.          << "    (以网络顺序显示)"<< endl;  
  64.     // 以二进制方式打开输出文件  
  65.     fOutfile.open(argv[2],ios::in|ios::out|ios::binary|ios::trunc);  
  66.     // 将pBuf中的数据报写入输出文件  
  67.     fOutfile.write((char *)pBuf, wLen+4);  
  68.     cout<< "数据报已成功保存在" << argv[2] << "文件中!" << endl;  
  69.     delete [] pBuf;        // 释放数据报缓冲区  
  70.     fInfile.close();    // 关闭输入文件流  
  71.     fOutfile.close();    // 关闭输出文件流  
  72.     return 0;  

 

你可能感兴趣的:(C++,算法,校验,Exercises,网际)