实验报告(3) ——首部检验和的编程实现 一、 实验人员 二、实验内容和要求 (一)实验内容 编写一个计算IP报文中首部校验和的程序,并且通过抓包随机选择一个数据报,提取出IP报文首部部分,根据程序计算出校验和的计算值,与报文中检验和对比,检验程序的正确性,计算结果为0保留数据报,否则丢弃。
(二)实验要求 1.掌握IP数据报的结构,通过wireshark抓包随机选择一个TCP数据报,提取IP报部分,并找到首部。
2.学习理解并运用IP首部校验和的计算算法,通过程序实现。 三、实验环境 Wireshark抓包工具 Microsoft visual studio 2010 C# 四、实验过程与结果分析 (一)实验过程 1.利用wireshark抓包分析包格式并提取IP首部 (1)IP数据报格式 如图1,IP数据报的首部为前20字节。因为首部校验和的计算只需要IP数据报的首部20个字节即可,数据部分不参加运算。所以只需要把IP数据包中的前20个16进制数提取出来作为运算的数据即可。
图 1 IP数据包格式 (2)抓包得到的数据报 图 2 TCP数据报 如图2,通过wireshark抓包,随机选择一个TCP数据报,根据TCP数据报的格式,我们分析数据报内容可以知道:首先开始6个字节为目的mac地址,后面6个字节为目的mac地址,后面2个字节为协议类型为0800,是IP协议包。所以再后面的20个字节就是IP数据报的首部部分。也就是实验所需要的数据。
IP首部为:450000280000400034060ab73baf84710a687191 并且,通过包的计算的校验和结果,此包被留下,校验和的值为0。也是座位后面对设计程序计算得到结果的一个验证。
2.设计计算首部校验和的的算法 (1)程序设计的函数模块关系 (2)算法流程 首先将IP首部即40位16进制的字符串转化为二进制字符串,然后以每16位为一个字段,分为10个二进制字段。存放在IP1~IP10字符串中;
然后求出每个字段的反码,根据IP数据报格式,可以知道IP6存放的就是校验和,将校验和IP6置位全0;
利用求反码和方法FanMa(),求出10个字段反码相加后的值string sum,然后对字符串sum取反,得到发送时所用的校验码。
假设接收端的IP首部与发送的是相同的,那么其10个字段的利用反码和算法求出的值应为0。
算法的流程与在发送端求出发送的校验码过程相同。
输入IP首部 string IP0 转化为二进制字符串并划分为10个字段IP1~IP10,函数Getziduan()
求字段反码,函数FanMa()
反码求和函数FanSum();FanSum0(); 求出由上一布得到的结果的反码,输出结果 (3)几个主要的方法 将16进制转化为二进制的字符串操作: public static string Change(char a) {
switch (a) { case '0': return "0000"; case '1': return "0001"; case '2': return "0010"; case '3': return "0011"; case '4': return "0100"; case '5': return "0101"; case '6': return "0110"; case '7': return "0111"; case '8': return "1000"; case '9': return "1001"; case 'a': return "1010"; case 'b': return "1011"; case 'c': return "1100"; case 'd': return "1101"; case 'e': return "1110"; case 'f': return "1111"; default: break; } return "xxxx"; } 求各字段的反码: public static string FanMa(string zd) { string buff0 = ""; char a; for (int i = 0; i < zd.Length; i++) { a = zd[i]; if (a == '0') { buff0 = buff0 + "1"; } else { buff0 = buff0 + "0"; } } return buff0; }
求各字段的和: public static string FanSum(string ip1, string ip2, string ip3, string ip4, string ip5, string ip6, string ip7, string ip8, string ip9, string ip10,int len) { string buff = ""; buff = FanSum0(ip1, ip2, len); buff = FanSum0(buff, ip3, len); buff = FanSum0(buff, ip4, len); buff = FanSum0(buff, ip5, len); buff = FanSum0(buff, ip6, len); buff = FanSum0(buff, ip7, len); buff = FanSum0(buff, ip8, len); buff = FanSum0(buff, ip9, len); buff = FanSum0(buff, ip10, len); return buff;
} public static string FanSum0(string ip1, string ip2,int len) { string sum = ""; char a; char b; string buff=""; string buff1=""; int k = 0; for (int i = len-1; i >=0; i--) { a = ip1[i]; b = ip2[i]; if (a == '0' && b == '1' && k == 0) { buff = buff + "1"; k = 0; continue; } if (a == '1' && b == '0' && k == 0) { buff = buff + "1"; k = 0; continue; } if (a == '1' && b == '1' && k == 0) { buff = buff + "0"; k = 1; continue; } if (a == '1' && b == '1' && k == 1) { buff = buff + "1"; k = 1; continue; } if (a == '1' && b == '0' && k == 1) { buff = buff + "0"; k = 1; continue; } if (a == '0' && b == '1' && k == 1) { buff = buff + "0"; k = 1; continue; } if (a == '0' && b == '0' && k == 1) { buff = buff + "1"; k = 0; continue; } if (a == '0' && b == '0' && k == 0) { buff = buff + "0"; k = 0; continue; }
} for (int j = len-1; j >= 0; j--) {
buff1 = buff1 + buff[j]; }
if (k == 1) sum = FanSum0(buff1, "0000000000000001", 16); else sum = buff1; return sum;
}
3.显示结果并与已知值对比 结果显示最后的校验和为0,这个数据包被留下,与实际情况相同,但此时还不能说明算法一定正确,因为我们还没有考虑数据包被丢弃的情况。 在数据包被丢弃的情况下,肯定是最终算的校验和不为0,也就是接受到的IP首部与发送的不同,因此我们只要将接收端用来计算校验和的字段IP1~IP10任意更改一位,使其与原来不同即可。我所做的更改为由原来IP1的反码:1011101011111111,在计算接收端的校验和时更改为:1011101011111110。 所得到的结果为:
所以算法是正确的。 (二)结果分析 通过数据包正确接收和被丢弃两种情况对设计的校验和计算算法的比较,计算的结果与实际情况校验和的值是一致的,因此在数据报在实际通信时,利用校验和的方式可以判定所接收的数据报是否发生了变化,保证了可靠性。同时校验和的方法又是比较简便快速的,减少了时间上的开销,因此是在实际情况下比较适用的方法。
五、小结
(一)实验中曾经出现的犯过的错误、遇到的问题及相应的解决方案。 1.在利用字符串计算字段相加求值时开始没有考虑到计算结果正好是实际值的倒叙,通过调试观察局部变量的方法发现了错误,将计算结果作了顺序的转换,