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

本文通过实例演示如何使用Windows API获取局域网内的主机MAC地址,并展示如何获取本机网卡详细信息。此外,还深入介绍了计算IP、TCP、UDP等协议头部校验和所使用的网际校验和算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> #include < WinSock2.h >
#include
< IPHlpApi.h >
#include
< iostream >
using namespace std;

#pragma comment(lib,"Iphlpapi")
#pragma comment(lib,"Ws2_32")

int _tmain( int argc,_TCHAR * argv[])
{
MIB_IPADDRTABLE
* pIPAddrTable = (MIB_IPADDRTABLE * )malloc( sizeof (MIB_IPADDRTABLE));
ULONGdwSize
= 0 ,dwRetVal = 0 ;
if (GetIpAddrTable(pIPAddrTable, & dwSize, 0 ) == ERROR_INSUFFICIENT_BUFFER)
{
free(pIPAddrTable);
pIPAddrTable
= (MIB_IPADDRTABLE * )malloc(dwSize);
}
if ((dwRetVal = GetIpAddrTable(pIPAddrTable, & dwSize, 0 )) == NO_ERROR)
{
ULONGulHostIp
= ntohl(pIPAddrTable -> table[ 0 ].dwAddr); // 本机IP
ULONGulHostMask = ntohl(pIPAddrTable -> table[ 0 ].dwMask); // 子网掩码
for (ULONGi = 1 ;i < ( ~ ulHostMask); ++ i)
{
static ULONGulNo = 0 ;
HRESULThr;
IPAddripAddr;
ULONGpulMac[
2 ];
ULONGulLen;
ipAddr
= htonl(i + (ulHostIp & ulHostMask));
memset(pulMac,
0xff , sizeof (pulMac));
ulLen
= 6 ;
hr
= SendARP(ipAddr, 0 ,pulMac, & ulLen); // 发送ARP请求
if (ulLen == 6 )
{
ulNo
++ ;
PBYTEpbHexMax
= (PBYTE)pulMac;
unsigned
char * strIpAddr = (unsigned char * )( & ipAddr);
printf(
" %d:MAC地址%02X:%02X:%02X:%02X:%02X:%02XIP地址%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 ]);
}
}
}
else
{
printf(
" 失败 " );
}
printf(
" 结束!/n " );
free(pIPAddrTable);
return 0 ;
}
2 ,获取本机网卡信息
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> #include < WinSock2.h >
#include
< IPHlpApi.h >
#include
< iostream >
using namespace std;

#pragma comment(lib,"Iphlpapi")
#pragma comment(lib,"Ws2_32")

int _tmain( int argc,_TCHAR * argv[])
{
PIP_ADAPTER_INFOpAdapterInfo;
PIP_ADAPTER_INFOpAdapter
= NULL;
DWORDdwRetVal
= 0 ;
pAdapterInfo
= (IP_ADAPTER_INFO * )malloc( sizeof (IP_ADAPTER_INFO));
ULONGulOutBufLen
= sizeof (IP_ADAPTER_INFO);
dwRetVal
= GetAdaptersInfo(pAdapterInfo, & ulOutBufLen);
if (dwRetVal == ERROR_BUFFER_OVERFLOW)
{
free(pAdapterInfo);
pAdapterInfo
= (IP_ADAPTER_INFO * )malloc(ulOutBufLen);
dwRetVal
= GetAdaptersInfo(pAdapterInfo, & ulOutBufLen);
}
if (dwRetVal == NO_ERROR)
{
pAdapter
= pAdapterInfo;
while (pAdapter)
{
printf(
" 适配器名称:/t%s/n " ,pAdapter -> AdapterName);
printf(
" 适配器描述信息:/t%s/n " ,pAdapter -> Description);
printf(
" MAC地址:/t%02X:%02X:%02X:%02X:%02X:%02X/n " ,pAdapter -> Address[ 0 ],pAdapter -> Address[ 1 ],
pAdapter
-> Address[ 2 ],pAdapter -> Address[ 3 ],pAdapter -> Address[ 4 ],pAdapter -> Address[ 5 ]);
printf(
" IP地址:/t%s/n " ,pAdapter -> IpAddressList.IpAddress.String);
printf(
" 子网掩码:/t%s/n " ,pAdapter -> IpAddressList.IpMask.String);
printf(
" 网关地址:/t%s/n " ,pAdapter -> GatewayList.IpAddress.String);
if (pAdapter -> DhcpEnabled)
{
printf(
" DHCPenabled:yes/n " );
printf(
" DHCP服务器:/t%s/n " ,pAdapter -> DhcpServer.IpAddress.String);
printf(
" 租约:%ld/n " ,pAdapter -> LeaseObtained);
}
else
{
printf(
" DHCPenabled:no/n " );
}
if (pAdapter -> HaveWins)
{
printf(
" HaveWins:Yes/n " );
printf(
" PrimaryWinsServer:/t%s/n " ,pAdapter -> PrimaryWinsServer.IpAddress.String);
printf(
" SecondaryServer:/t%s/n " ,pAdapter -> SecondaryWinsServer.IpAddress.String);
}
else
{
printf(
" HaveWins:No/n " );
}
pAdapter
= pAdapter -> Next;
}
}
else
{
printf(
" 失败/n " );
}
return 0 ;
}

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

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> #include < iostream >
#include
< fstream >
using namespace std;
#include
< winsock.h > // 本机字节序转换为网络字节序:htons
#pragma comment(lib,"WS2_32.LIB")

/* *************************************************************************
*计算给定数据的校验和
*
*输入参数:
*pBuffer指向需要校验的数据缓冲区
*nSize需要校验的数据的大小,以字节为单位
*
*返回值:
*16位的校验结果
*
*************************************************************************
*/
unsigned
short checksum_calculating(unsigned short * pBuffer, int nSize)
{
unsigned
long dwCksum = 0 ; // 32位累加和
// 以两字节为单位反复累加
while (nSize > 1 )
{
dwCksum
+= * pBuffer ++ ;
nSize
-= sizeof (unsigned short );
}
// 如果总字节数为奇数则加上最后一个字节
if (nSize)
{
dwCksum
+= * (unsigned char * )pBuffer;
}
// 将位累加和的高位与低位第一次相加
dwCksum = (dwCksum >> 16 ) + (dwCksum & 0xffff );
// 将上一步可能产生的高位进位再次与低位累加
dwCksum += (dwCksum >> 16 );
// 返回位校验和
return (unsigned short )( ~ dwCksum);
}


int main( int argc, char * argv[])
{

// 创建输入文件流
ifstreamfInfile;
fstreamfOutfile;
// 创建输出文件流
fInfile.open(argv[ 1 ],ios:: in | ios::binary); // 以二进制方式打开指定的输入文件
fInfile.seekg( 0 ,ios::end); // 把文件指针移到文件末尾
unsigned short wLen = (unsigned short )fInfile.tellg(); // 取得输入文件的长度
fInfile.seekg( 0 ,ios::beg); // 文件指针位置初始化
// 定义数据报缓冲区,缓冲区大小为+wLen,其中为数据报类型字段、长度字段
// 以及校验和字段的长度和,wLen为数据字段长度,即输入文件长度(以字节为单位)
char * pBuf = new char [ 4 + wLen];
pBuf[
0 ] = unsigned char ( 0xab ); // 给数据报类型字段赋值,这里随便弄了个0Xab
pBuf[ 1 ] = unsigned char (wLen); // 给数据报长度字段赋值
* (unsigned short * )(pBuf + 2 ) = 0 ; // 计算校验和之前,校验和字段先置为0
fInfile.read(pBuf + 4 ,wLen); // 根据输入文件填充数据报的数据字段
// 计算校验和并把结果填入到数据报的校验和字段
* (unsigned short * )(pBuf + 2 ) = checksum_calculating((unsigned short * )pBuf, 4 + wLen);
// 输出校验和计算结果
cout.width( 4 );
cout
<< " 校验和为:x " << hex << htons( * (unsigned short * )(pBuf + 2 ))
<< " (以网络顺序显示) " << endl;
// 以二进制方式打开输出文件
fOutfile.open(argv[ 2 ],ios:: in | ios:: out | ios::binary | ios::trunc);
// 将pBuf中的数据报写入输出文件
fOutfile.write(( char * )pBuf,wLen + 4 );
cout
<< " 数据报已成功保存在 " << argv[ 2 ] << " 文件中! " << endl;
delete[]pBuf;
// 释放数据报缓冲区
fInfile.close(); // 关闭输入文件流
fOutfile.close(); // 关闭输出文件流
return 0 ;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值