IP首部校验和计算与程序设计

一,感受与实验基本要求

这是我《计算机网络》这门课程的第二个实验,说实话这个实验吧,我觉得我完成的不是特别的好,好些地方都是取巧,并不是真正意义上的完成这个实验。但是我觉得还是把这个实验的前后写下来,毕竟是见证自己的一个成长。接下来我会先介绍一下实验要求与实验内容。

1.1 实验目的

1.掌握IP协议包结构;
2.学会IP协议包首部数据校验

1.2 实验要求

针对 IP 协议包结构,2 人一组,共同设计良好的人机界面,并实现首部校验和的计算程序。功能包括:
(1)在界面上,用户能够输入或编辑 IP 协议包各字段数据,例如,“协议”字段应该是下拉选择 方式;
(2)程序能够自动检查 IP 地址的合法性,且主机可用;
(3)能够自动计算首部校验和的值,并显示计算结果;
(4)必须验证程序的正确性:基于 Wireshark 工具所抓取的真实 IP 包首部数据,能够成功检验首 部校验和字段的计算程序。

二,实验内容

2.1 IP首部的基本校验方法

1,把IP数据包的校验和字段置为0;   
2,把首部看成以16位为单位的数字组成,依次进行二进制求和(注意:求和时应将最高位的进位保存,所以加法应采用32位加法);   
3,将上述加法过程中产生的进位(最高位的进位)加到低16位(采用32位加法时,即为将高16位与低16位相加,之后还要把该次加法最高位产生的进位加到低16位)   
4,将上述的和取反,即得到校验和。

2.2流程图

如图是流程图,可以先看看

2.3编程思路

2.3.1得到数据并进行标准化

首先,程序需要从交互界面获取十进制表示的IP头除了校验和的各部分数据。将这些数据转换成16进制形式的字符串,进行标准化,若不进行标准化,则容易在不同信息合并中因为结束出现问题。

String type1 = Edition.getSelectedItem();//获取所选内容
			String HeadLength1 = HeadLength.getText();//获取文本框内的内容
			String SeverType1 = SeverType.getSelectedItem();//下拉框的
			SeverType1 = flags_change(SeverType1);
			Sum[0] = Integer.parseInt(Connecting(type1,HeadLength1,SeverType1),16);//将版本,首部长度与服务结合,形成sum1。

如上,采集下拉框Edition(版本)(使用getSelectedItem()函数)、SeverType(服务类型)与文本框HeadLength(头部长度)(使用getText()函数)的填入字符串,然后通过Connecting()函数将字符串拼接起来,并通过parseInt()内置函数将其转换成16进制存储在sum[]数组中。

  采集
String S_IP1 = Length(encodeHEX(Integer.parseInt(S_IP_add1.getText())));
 标准化函数
public static String Length(String a) {
   	String b = null;
   	if(a.length()==1) b = "0" + a;
   	else if(a.length() == 2) b = a;
   	return b;
   }

是采集IP地址的第一个框的内容,而Length()的作用是标准化,将其表标准化为“xx”的形式,方便后面的拼接操作。

2.3.2 信息合并后,把字符串转换成十进制,并存储在数组中。

String AllLength2 = encodeHEX(Integer.parseInt(AllLength1));

将两个IP的范围的16进制形式通过Connecting()函数连接,通过parseInt(String,16)转化成10进制的int型,并存储在数组Sum[]中。
parseInt(String,16)的作用是把以字符串形式存储的16进制转换成10进制的整形。

public String Connecting(String a,String b,String c) {//将首部连接
		String d;
		if(c != null) {
			d = a+b+c;
		}else {
			d = a+b;
		}
		return d;
	}

看这个函数,可以决定是连接多少个首部,并存储在字符串中。

2.3.3 进行求和运算。用循环方式对数组内的10进制进行求和,并转换成字符串形式的16进制。

String SUM = count(Sum);

这是对Sum数组进行求和运算,Sum是一个数组,里面存着首部除了校验和之外的全部信息的十进制形式。

//先用10进制首部校验求和,然后转化成16进制
	public static String count(int[]  a) {
		int sum = 0;
		String end;
		for(int i = 0;i < 9;i++) {
			sum += a[i];
		}
		end = encodeHEX(sum);
		return end;
	}

为count()函数内部图,利用for循环将数组内部的10进制数字求和。这里返回的是和的16进制形式。

2.3.4 判断是否产生溢出,若产生溢出,依次将最高位的数字加在低位上,直到满足4位的16进制。

if(SUM.length()>4) {//若字长大于4位,则进行进位,将最高位加到最低位
				String s = SUM.substring(SUM.length()-4);
				String a = SUM.substring(0,1);
				int i = Integer.parseInt(s,16);
				int j = Integer.parseInt(a,16);
				SUM = encodeHEX(i+j);
			}

若字长大于4位,则进行切片操取出最高位和后四位,然后换成10进制后相加并换成16进制。

2.3.5 对进位后的和用ffff减去,所得的差即取反。

下面展示一些 内联代码片

//对16进制取反
	public String qufan(String a) {
		String m;
		int i,j = 0xffff;
		i = Integer.parseInt(a,16);
		m = encodeHEX(j-i);
		return m;
	}

如图,是进行取反的函数,将字符串a转换成10进制的整数后,用0xffff的10进制整数减去,再转换成16进制,即可得到去饭后的结果。此时整个运行结果结束。

三,程序运行界面与结果

3.1运行初始界面

程序的初始界面
在这里插入图片描述
如图,因为一个IP地址不合法(>255),检测出后进行提示,提示重新输入。
在这里插入图片描述
如上图,因为IP地址不合法(输入中有字母),检测出后进行提示,提示重新输入。
接下来我会简单介绍下判断是否合法是如何实现的。

//判断输入的IP是否全为10进制
	public boolean judge_ip(String a) {
		boolean b = null != null;
		for(int i=0 ; i<a.length() ; i++){
		if(Character.isLetter(a.charAt(i))) b = true;//用char包装类中的判断字母的方法判断每一个字符
		else b = false;
		}
		return b;
	}
	
	//判断IP是否合法
	public boolean Judge(String a) {
		int b = Integer.parseInt(a);
		if(b>=0&&b<=255) return true;
		else return false;
	}

如上,是进行检验的两个函数,第一个函数judge_ip()作用是检验IP地址是否均为数字10进制,若检测到字符则返回true;而第二个函数比较简单,是接受字符串的数字,换成整形后判断范围,若在范围内则说明IP正确,返回true,反正不正确。两者均返回布尔值,通过布尔值判断。

boolean judge1 = judge_ip(S_IP_add1.getText())||judge_ip(S_IP_add2.getText())||judge_ip(S_IP_add3.getText())||judge_ip(S_IP_add4.getText()||judge_ip(T_IP_add1.getText())||judge_ip(T_IP_add2.getText())||judge_ip(T_IP_add3.getText())||judge_ip(T_IP_add4.getText());
			if(judge1 == true) JOptionPane.showMessageDialog(null, "地址输入不合法!请重新输入");

如上,是进行是否均为10进制的判断。通过“或运算”,检查是否用10进制输入。因为judge_ip()函数是有字符就返回true,因此只需有一个true,则抛出提示,提示输入错误。

boolean judge = Judge(S_IP_add1.getText())&&Judge(S_IP_add2.getText())&&Judge(S_IP_add3.getText())&&Judge(S_IP_add4.getText())&&Judge(T_IP_add1.getText())&&Judge(T_IP_add2.getText())&&Judge(T_IP_add3.getText())&&Judge(T_IP_add4.getText());
			if(judge == false) JOptionPane.showMessageDialog(null, "地址输入不合法!请重新输入");

如图,解释Judge()函数的用法,采用“与运算”。因为该函数在范围内为真,因此需要全部为true才可以通过。假若有一个为假,则judge1会返回false,抛出提示,提示输入错误。

利用wireshark进行抓包,验证正确性

在这里插入图片描述
在这里插入图片描述
可以看出,首部校验和码与wireshark抓包是符合的,因此可以说程序成功了。

后言

这次实验我还是比较高兴的,因为我独立完成了它,而且在这中间,我对Java的JFrame框架更熟练了。而且我也明白了IP包首部的数据所代表的意思。总的来说,通过这次实验,我在编程方面和就《计算机网络》的知识积累了一些,毕竟不积硅步,无以至千里。
还有,这个程序其实有点问题,就是当求和比较大的时候,这个时候校验码会错误。
在这里插入图片描述
如图,就出现了错误,但我还没修好。也许以后会修修吧。
各位大佬轻喷,我只是一个大三学生,而且在学校的学习中经常划水,要是哪里写的不好请多多斧正。

程序原码

package 计网实验;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Frist_ extends JFrame{
	JPanel jp1,jp2,jp3,jp4,jp5,jp6,jp7,jp8,jp9,jp10,jp11,jp12,jp13,jp14;
	JLabel P_IP,F_length,S_Type,A_Length,Source_IP_add,Target_IP_add,sign,offset,ttl,mark,choose,Head_checksum;
	JButton Calculation;
	JTextField HeadLength,AllLength,S_IP_add1,S_IP_add2,S_IP_add3,S_IP_add4,T_IP_add1,T_IP_add2,T_IP_add3,T_IP_add4,Sign,Offset,TTL,Mark,Head_c;//SeverType;
	Choice choosing,Edition,SeverType;
	int[] Sum = new int[9];//各个码的10进制数存储在sum内
	final Container c = getContentPane();//定义组件,并且为添加组件时使用
	
	void init() {
		Calculation.addActionListener(new MyListener());;
	}
	//面板设计
	public Frist_ () {
		this.setTitle("IP包分析");
		this.setSize(550, 300);
		this.setResizable(false);//将窗口大小锁定
		
		jp1 = new JPanel();
	    P_IP = new JLabel("版本:");
		Edition = new Choice();
		Edition.addItem("4");
		Edition.addItem("6");
		jp1.add(P_IP);
	    jp1.add(Edition);
	    jp1.setBounds(10,10,100,50);
	    
	    jp5 = new JPanel();
	    F_length = new JLabel("首部长度:");
	    HeadLength = new JTextField(5);
	    jp5.add(F_length);
	    jp5.add(HeadLength);
	    jp5.setBounds(100, 10, 150, 50);
	    
	    jp6 = new JPanel();
	    S_Type = new JLabel("服务类型:");
	    SeverType = new Choice();
	    SeverType.addItem("000");
	    SeverType.addItem("001");
	    SeverType.addItem("010");
	    SeverType.addItem("011");
	    SeverType.addItem("100");
	    SeverType.addItem("101");
	    SeverType.addItem("110");
	    SeverType.addItem("111");
	    //SeverType = new JTextField(5);
	    jp6.add(S_Type);
	    jp6.add(SeverType);
	    jp6.setBounds(240,10, 150,50);
	    
	    jp7 = new JPanel();
	    A_Length = new JLabel("总长度:");
	    AllLength = new JTextField(5);
	    jp7.add(A_Length);
	    jp7.add(AllLength);
	    jp7.setBounds(370,10,160,50);
	    
	    jp13 = new JPanel();
	    mark = new JLabel("标识:");
	    Mark = new JTextField(10);
	    jp13.add(mark);
	    jp13.add(Mark);
	    jp13.setBounds(10,60,150, 50);
	    
	    jp10 = new JPanel();
	    sign = new JLabel("标志:");
	    Sign = new JTextField(3);
	    jp10.add(sign);
	    jp10.add(Sign);
	    jp10.setBounds(150, 60, 100,50);
	    
	    jp11 = new JPanel();
	    offset = new JLabel("偏位移:");
	    Offset = new JTextField(3);
	    jp11.add(offset);
	    jp11.add(Offset);
	    jp11.setBounds(250, 60, 100, 50);
	    
	    jp12 = new JPanel();
	    ttl = new JLabel("生存时间:");
	    TTL = new JTextField(3);
	    jp12.add(ttl);
	    jp12.add(TTL);
	    jp12.setBounds(10,110,110, 50);
	    
	    jp2 = new JPanel();
	    choose = new JLabel("协议:");
	    choosing = new Choice();
	    choosing.addItem("ICMP");
	    choosing.addItem("IGMP");
	    choosing.addItem("GGP");
	    choosing.addItem("IP");
	    choosing.addItem("TCP");
	    choosing.addItem("EGP");
	    choosing.addItem("UDP");
	    choosing.addItem("IGRP");
	    choosing.addItem("OSPF");
	    jp2.add(choose);
	    jp2.add(choosing);
	    jp2.setBounds(100, 110, 130,50);
	    
	    jp14 = new JPanel();
	    Head_checksum = new JLabel("首部校验和:");
	    Head_c= new JTextField(5);
	    Head_c.setEditable(false);
	    jp14.add(Head_checksum);
	    jp14.add(Head_c);
	    jp14.setBounds(200,110, 200, 60);
	    
	    jp8 = new JPanel();
	    Source_IP_add = new JLabel("源IP地址:");
	    S_IP_add1 = new JTextField(3);
	    S_IP_add2 = new JTextField(3);
	    S_IP_add3 = new JTextField(3);
	    S_IP_add4 = new JTextField(3);
	    jp8.add(Source_IP_add);
	    jp8.add(S_IP_add1);
	    jp8.add(S_IP_add2);
	    jp8.add(S_IP_add3);
	    jp8.add(S_IP_add4);
	    jp8.setBounds(10,160, 230,50);
	    
	    jp9 = new JPanel();
	    Target_IP_add = new JLabel("目的IP地址");
	    T_IP_add1 = new JTextField(3);
	    T_IP_add2 = new JTextField(3);
	    T_IP_add3 = new JTextField(3);
	    T_IP_add4 = new JTextField(3);
	    jp9.add(Target_IP_add);
	    jp9.add(T_IP_add1);
	    jp9.add(T_IP_add2);
	    jp9.add(T_IP_add3);
	    jp9.add(T_IP_add4);
	    jp9.setBounds(10,210, 230,50);
	    
	    jp3 = new JPanel();
	    Calculation = new JButton("计算首部校验和");
	    jp3.add(Calculation);
	    jp3.setBounds(290,180,110, 80);
	    
	   c.setLayout(null);//布局,为null方便进行自定义布局
	   c.add(jp1);c.add(jp5);c.add(jp6);
	   c.add(jp7);c.add(jp13);c.add(jp10);
	   c.add(jp11);c.add(jp12);c.add(jp2);
	   c.add(jp8);c.add(jp9);c.add(jp3);
	   c.add(jp14);
	   c.add(new JPanel());
	   this.setVisible(true);
	}
	
	class MyListener implements ActionListener{
		public void actionPerformed(ActionEvent e) {
			String type1 = Edition.getSelectedItem();//获取所选内容
			String HeadLength1 = HeadLength.getText();//获取文本框内的内容
			String SeverType1 = SeverType.getSelectedItem();//下拉框的
			SeverType1 = flags_change(SeverType1);
			Sum[0] = Integer.parseInt(Connecting(type1,HeadLength1,SeverType1),16);//将版本,首部长度与服务结合,形成sum1。
			//String SeverType1 = SeverType.getText();//手动输入的
			//Sum[0] = Integer.parseInt(Connecting(type1,HeadLength1,SeverType1),16);//将版本,首部长度与服务结合,形成sum1。(手动输入的)
			String AllLength1 = AllLength.getText();
			String AllLength2 = encodeHEX(Integer.parseInt(AllLength1));//读取总长度,并转换成字符串形式的16进制
			Sum[1] = Integer.parseInt(AllLength2,16);
			String Mark1 = Mark.getText();
			String Mark2 = encodeHEX(Integer.parseInt(Mark1));//读取标识,并转换成字符串形式的16进制
			Sum[2] = Integer.parseInt(Mark2,16);
			String Sign1  = Sign.getText();
			String Offset1 = Offset.getText();
			Sum[3] = Integer.parseInt(Connecting(Sign1,Offset1,null),16);//将标志与偏位移连接			
			String TTL1 = TTL.getText();
			String TTL2 = encodeHEX(Integer.parseInt(TTL1));
			String document1 = Choose(choosing.getSelectedItem());
			Sum[4] = Integer.parseInt(Connecting(TTL2,document1,null),16);
			boolean judge1 = judge_ip(S_IP_add1.getText())||judge_ip(S_IP_add2.getText())||judge_ip(S_IP_add3.getText())||judge_ip(S_IP_add4.getText())
                 	||judge_ip(T_IP_add1.getText())||judge_ip(T_IP_add2.getText())||judge_ip(T_IP_add3.getText())||judge_ip(T_IP_add4.getText());
			if(judge1 == true) JOptionPane.showMessageDialog(null, "地址输入不合法!请重新输入");
			String S_IP1 = Length(encodeHEX(Integer.parseInt(S_IP_add1.getText())));
			String S_IP2 = Length(encodeHEX(Integer.parseInt(S_IP_add2.getText())));
			String S_IP3 = Length(encodeHEX(Integer.parseInt(S_IP_add3.getText())));
			String S_IP4 = Length(encodeHEX(Integer.parseInt(S_IP_add4.getText())));
			String T_IP1 = Length(encodeHEX(Integer.parseInt(T_IP_add1.getText())));
			String T_IP2 = Length(encodeHEX(Integer.parseInt(T_IP_add2.getText())));
			String T_IP3 = Length(encodeHEX(Integer.parseInt(T_IP_add3.getText())));
			String T_IP4 = Length(encodeHEX(Integer.parseInt(T_IP_add4.getText())));
			//判断有没有不合法的输入
			boolean judge = Judge(S_IP_add1.getText())&&Judge(S_IP_add2.getText())&&Judge(S_IP_add3.getText())&&Judge(S_IP_add4.getText())
		                 	&&Judge(T_IP_add1.getText())&&Judge(T_IP_add2.getText())&&Judge(T_IP_add3.getText())&&Judge(T_IP_add4.getText());
			if(judge == false) JOptionPane.showMessageDialog(null, "地址输入不合法!请重新输入");
			Sum[5] = Integer.parseInt(Connecting(S_IP1,S_IP2,null),16);
			Sum[6] = Integer.parseInt(Connecting(S_IP3,S_IP4,null),16);
			Sum[7] = Integer.parseInt(Connecting(T_IP1,T_IP2,null),16);
			Sum[8] = Integer.parseInt(Connecting(T_IP3,T_IP4,null),16);
			String SUM = count(Sum);
			if(SUM.length()>4) {//若字长大于4位,则进行进位,将最高位加到最低位
				String s = SUM.substring(SUM.length()-4);
				String a = SUM.substring(0,1);
				int i = Integer.parseInt(s,16);
				int j = Integer.parseInt(a,16);
				SUM = encodeHEX(i+j);
			}
			SUM = qufan(SUM);
			Head_c.setText("0x"+SUM);                                                                                                                                                                                                                                                                                                                                                                    
		}
	}
	
	//将首部连接
	public String Connecting(String a,String b,String c) {
		String d;
		if(c != null) {
			d = a+b+c;
		}else {
			d = a+b;
		}
		return d;
	}
	
	//判断输入的IP是否全为10进制
	public boolean judge_ip(String a) {
		boolean b = null != null;
		for(int i=0 ; i<a.length() ; i++){
		if(Character.isLetter(a.charAt(i))) b = true;//用char包装类中的判断字母的方法判断每一个字符
		else b = false;
		}
		return b;
	}
	
	//判断IP是否合法
	public boolean Judge(String a) {
		int b = Integer.parseInt(a);
		if(b>=0&&b<=255) return true;
		else return false;
	}
	
	//不同协议的不同数
	public String Choose(String a) {
		String b = null;
		if(a.equals("ICMP" )) b = "01";
		else if(a.equals("IGMP")) b = "02";
		else if(a.equals("GGP")) b = "03";
		else if(a.equals("IP")) b = "04";
		else if(a.equals("TCP")) b = "06";
		else if(a.equals("EGP")) b = "08";
		else if(a.equals("UDP")) b = "17";
		else if(a.equals("IGPR")) b = "88";
		else if(a.equals("OSPF")) b = "89";
		return b;
	}
	//不同标志不同的数
	public String flags_change(String a) {
		String b = null;
		if(a.equals("000" )) b = "00";
		else if(a.equals("001")) b = "01";
		else if(a.equals("010")) b = "02";
		else if(a.equals("011")) b = "03";
		else if(a.equals("100")) b = "04";
		else if(a.equals("101")) b = "05";
		else if(a.equals("110")) b = "06";
		else if(a.equals("111")) b = "07";
		return b;
		
	}
	
	//将10进制转换成16进制
	public static String encodeHEX(int numb) {   //注意,这里再写count函数时添加了一个静态,如果出现bug把静态删掉!!!
		String hex= Integer.toHexString(numb);
		return hex;
	}
	
	//先用10进制首部校验求和,然后转化成16进制
	public static String count(int[]  a) {
		int sum = 0;
		String end;
		for(int i = 0;i < 9;i++) {
			sum += a[i];
		}
		end = encodeHEX(sum);
		return end;
	}
	
	//特别的对IP进行补位运算,每个IP以4位为主,若小于2位,则进行补位
	public static String Length(String a) {
		String b = null;
		if(a.length()==1) b = "0" + a;
		else if(a.length() == 2) b = a;
		return b;
	}
	
	//对16进制取反
	public String qufan(String a) {
		String m;
		int i,j = 0xffff;
		i = Integer.parseInt(a,16);
		m = encodeHEX(j-i);
		return m;
	}
	
	public static void main(String[] args) {
		new Frist_().init();
	}	
}

  • 8
    点赞
  • 0
    评论
  • 18
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值