四 川 理 工 学 院
课 程 设 计 书
学院 计算机学院
专业 计算科学与技术
班级 2009级5班
题目 IP地址的合法性及子网判断
教师 王非何绍荣陈超
学生 陶宁于友为吴小平
目录
第一章 概述 1
1.1课程设计目的... 1
1.2 课程设计的要求... 1
第二章 具体设计... 3
2.1设计思路... 3
2.2流程图如下:... 4
2.3核心源代码分析与说明... 9
第三章 结果分析与总结... 18
3.1结果与分析... 18
3.2总结... 22
3.2参考文献... 24
小组成员及分工:
陶宁:程序代码的编写、设计思路、算法设计、总结
于友为:程序流程图、资料收集、程序测试、算法设计、总结
吴小平:课题报告的书写、资料收集、算法设计、总结
第一章 概述
1.1课程设计目的
本设计要求编写程序,判断一个IP地址是否合法,并判断该地址是否是属于一个给定的子网。从而考察读者是否对IP地址概念及子网划分有非常清楚的认识。
1.2 课程设计的要求
在掌握IP 地址的表示方法及指望划分方法的基础上,按如下要求完成程序。
1) 命令行格式:ip_test subnet/mask ip_addr
其中,ip_test为程序名;subnet为子网好;mask是一个数值,代表子网掩码连续1 的个数;ip_addr是要测试的IP地址。
例如,要测试的IP地址为202.113.16.10,子网号为202.113.16.0,子网掩码为255.255.255.0,则没命令行为ip_test202.113.16.0/24 202.113.16.10.(因为255.255.255.0是连续的24个1,所以用24表示。)
2) 判断subnet和ip_addr的合法性。
在判断IP地址的合法性是要自行编写代码,不要用任何的inet函数。判断是要考虑全面,比如以下IP 的地址的合法性:
123..2.1
123.23¥.2.1
123.23.45.2.1
3) 判断掩码的合法性。
4) 在IP地址合法的前提下,判断ip_addr是否属于子网subnet。
5) 输入命令行中的ip是否合法,掩码是否合法(可适当给出不合法原因)以及ip_addr是否属于子网subnet。
第二章 具体设计
2.1设计思路
根据以上的要求,并结合当前的所学的一些知识和技术,我们最终通过另一种方式来实现相同的功能,但代码更加优化,高效,方便用户的窗体程序。
对于以上的要求,设计思路如下:
1. 程序大致流程
1) 分别判断子网号,掩码和ip地址的基本格式的合法性。
2) 如果三者的基本格式都合法,才调用判定“IP地址是否为子网成员”的函数。该函数同时判别子网号和掩码是否匹配,子网号为全0或全1,主机号为全0、全1。
2. 核心代码及说明
判断ip地址是否合法,才调用判定“IP地址是否为子网成员”的函数。该函数同时判断子网号与掩码是否匹配,子网号为全0或全1,主机号为全0、全1.
3. 核心代码及说明
1) 判断IP地址是否合法。
一般来说,我们先检查最明显的错误。例如,可以先判断IP中“.”的个数是否正确;接着,以“.”为标志将IP字符串按节分开;然后,看该IP是否为4段,再判断各节是否是0~255间的整数。关于网络号、主机号全0全1等问题要在后面结合子网掩码一起判断。
这里德关键是怎么样将IP字符串以“.”为标志按节分开,这要用到函数strtok(char * strToken,const char * strDelimit)。第一个参数是给定的串,第二个参数是分隔符集合,该函数的功能就是以strDelimit中包含的任意字符为分隔符,在strToken中寻找下一个token。每一次调用strtok后,都会在strToken中插入一个NULL字符,所以,如果要读取下一个stoken,接下来调用strtok时第一个参数用NULL。
这样,IP串的每一段都放到dot数组里了,下面判断是否有非法字符,每一段的值是否在0~255之间,就很容易了。
2) 判断IP地址是否为子网成员,判断子网号与掩码是否匹配,以及子网号、主机号全0、全1的问题
2.2流程图如下:
当用户启动程序后(如图2.1),在输入框中输入IP、子网、子网掩码,然后点击测试,验证其合法性。在此过程中调用子程序isLegal(如图2.2)在子程序中判断输入的IP地址是否合法(其中包括:1.判断输入的IP长度是否合法;2.判断输入的IP是否含有非法字符;3.判断输入的IP格式是否正确;4.判断输入的IP是否已转化为Ipaddress的实例)。跳回到主程序中继续执行,如果IP、子网、子网掩码都合法,调用子程序subChecked(如图2.3),在子程序中完成判断子网号和子网掩码是否匹配,如果匹配则进一步判读子网号是否为全0或全1,否则输出子网号和子网掩码不匹配提示。接下来,如果子网号不为全0或全1则将IP和掩码做“与”运算,结果和子网号比较,否则输出子网号为全0或全1提示。再接下来,判断比较结果(IP和掩码做“与”运算的结果和子网号的比较结果)是否相等,如果相等则判断主机号是否为全0或全1,如果不相等则输出“IP不属于该子网”。最后,如果主机号为全0或全1则输出主机号为全0或全1提示,如果主机号不为全0或全1则输出“IP属于该子网”。
子程序执行完后返回主程序中继续执行,然后完成ip地址是否属于给定子网的判定
图2.2-子程序islegal流程图
图2.3-子流程图subChecked
2.3核心源代码分析与说明
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Text.RegularExpressions;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string ipAddress;//声明ip地址
string childIp; //声明子网号
string yanma;//声明子网掩码
/*以下代码为单击检测按钮后执行相关的操作,程序首先清空
* 显示框listbox中的数据,然后检测由用户输入的ip地址
* 子网号,以及子网掩码的合法性,通过调用函数islegal来初步检测,
* 合法返回true,不合法返回false,并输出错误
* 信息,若验证通过,计算ip地址的二进制形式并输出,然后通过
* 给出的数据将ip地址与子网掩码进行与运算,并将结
* 果与给出的子网号进行比较,判断并输出该ip地址是否属于
* 给定的子网号
*/
private void button1_Click(object sender, EventArgs e)
{
//清空listBox中的内容
this.listBox1.Items.Clear();
//获取用户输入的ip地址
ipAddress = txtIp.Text;
//获取子网号
childIp = txtChild.Text;
//获取子网掩码
yanma = txtYanma.Text;
//判断输入的ip地址,子网号,子网掩码是否合法
if (!islegal(ipAddress))
{
MessageBox.Show("ip地址输入错误!", "提示");
return;
}
else if (!islegal(childIp))
{
MessageBox.Show("子网号输入错误!", "提示");
return;
}
else if (!islegal(yanma))
{
MessageBox.Show("子网掩码错误!", "提示");
return;
}
else
{
LbAdd("本次计算结果如下:");
string stp = ChangeIP(ipAddress);
GetIpStyle(stp);
LbAdd(ipAddress + "转化为二进制形式为:");
LbAdd(stp);
//string sto = ChangeIP(yanma);
//LbAdd(yanma + "转化为二进制形式为: "+sto);
// LbAdd("与子网掩码进行与运算~~");
//MessageBox.Show(stp);
Byte []ip3=IPAddress.Parse(ipAddress).GetAddressBytes();
//foreach (byte b in ip3)
//{
// LbAdd(b.ToString());
//}
//double ip2 =Convert.ToDouble((ChangeIP(ipAddress)));
//double cip2 = Convert.ToDouble((ChangeIP(childIp)));
//double ym2 = Convert.ToDouble((ChangeIP(yanma)));
//MessageBox.Show(ip2.ToString());
uint ip = GetBinaryIp(ipAddress);
uint cip = GetBinaryIp(childIp); f
uint ym = GetBinaryIp(yanma);
/*以下代码为子程序SubChecked中的内容,此处为方便编写,
* 直接写在了主程序体中,基本思路是按流程图走的,首先判断
* 子网号与掩码是否匹配,接着检测子网号是否为全0或全1的情况,
* 接着检测主机号为全0或全1的情况,最后判断计算出的子网号是
* 否与给定的子网号相同,并输出结果信息
*/
//说明子网号与掩码不匹配
if((cip|ym)!=ym)
{
LbAdd("子网号与掩码不匹配,error!");
Console.WriteLine("{0}", (ip | ym));
Console.WriteLine("{0}", ym);
return;
}
//说明子网号全1
if((cip^ym)==0)
{
LbAdd("子网号全1,error!");
return;
}
//说明子网号全0
if((cip&ym)==0)
{
LbAdd("子网号全为0,error!");
return;
}
//ip和掩码做"与"运算,结果和子网号比较
if (cip==(ip&ym))
{
//MessageBox.Show("success");
//说明主机号全0
if ((ip | ym) == ym)
{
LbAdd("主机号为全0,error!");
return;
}
//说明主机号全1
if ((ip | ym) == 0xffffffff)
{
LbAdd("主机号全1,error!");
return;
}
LbAdd("该ip 属于子网 "+childIp);
return;
}
else
{
//MessageBox.Show("faile");
LbAdd("该ip不属于子网" + childIp);
}
}
//Convert.ToString(11, 2);
}
/// <summary>
/// 判断输入的IP地址类型是否合法,合法返回TRUE,不合法返回FALSE
/// </summary>
/// <param name="strIp">传入待验证IP</param>
/// <returns></returns>
private bool islegal(string strIp)
{
IPAddress myip;
int length = ChangeIP(strIp).Length;
//用正则表达式判断输入的ip类型是否合法
Regex regIp=new Regex(@"\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3}");
if(!regIp.IsMatch(strIp))
{
return false;
}
//判断输入的ip型字符串能否转化为ip型
else if (!IPAddress.TryParse(strIp, out myip))
{
return false;
}
//判断ip长度是否合法
else if(length!=32)
{
return false;
}
// else if (GetBinaryIp(strIp))
else
{
return true;
}
}
/// <summary>
/// 将十进制ip 转化为二进制
/// </summary>
/// <param name="strIP"></param>
/// <returns></returns>
public string ChangeIP(string strIP)
{
string strHtml = "";
try
{
string[] IParray = strIP.Split('.'); //拆分IP
int IP1 = Convert.ToInt32(IParray[0]);
int IP2 = Convert.ToInt32(IParray[1]);
int IP3 = Convert.ToInt32(IParray[2]);
int IP4 = Convert.ToInt32(IParray[3]);
string strIP1 = GetIP(IP1);
string strIP2 = GetIP(IP2);
string strIP3 = GetIP(IP3);
string strIP4 = GetIP(IP4);
//将拆分后的ip地址重新组合起来
strHtml = strIP1 + strIP2 + strIP3 + strIP4;
}
catch
{
MessageBox.Show("输入地址类型有错!,可能包含其他字符。","警告");
}
return strHtml;
}
/// <summary>
/// 得到二进制IP
/// </summary>
/// <param name="IP"></param>
/// <returns></returns>
public string GetIP(int IP)
{
string strIP = Convert.ToString(IP, 2);
int len = strIP.Length;
if (len < 8)
{
for (int i = 0; i < 8 - len;i++)
{
strIP = "0" + strIP;
}
}
return strIP;
}
/// <summary>
/// 直接转化为二进制形式的ip地址,返回无符号整数形式
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
private uint GetBinaryIp(string str)
{
IPAddress myip = IPAddress.Parse(str);
uint iptest = BitConverter.ToUInt32(myip.GetAddressBytes(), 0);
return iptest;
}
/// <summary>
/// 向listbox中添加测试的结果
/// </summary>
/// <param name="str"></param>
private void LbAdd(string str)
{
listBox1.Items.Add(str);
}
/// <summary>
/// 获取ip地址的类型
/// </summary>
/// <param name="str"></param>
private void GetIpStyle(string str)
{
if (str.StartsWith("0"))
{
LbAdd("该ip属于A类网络");
}
else if (str.StartsWith("10"))
{
LbAdd("该ip属于B类网络");
}
else if (str.StartsWith("110"))
{
LbAdd("该ip属于C类网络");
}
else if (str.StartsWith("1110"))
{
LbAdd("该ip属于D类网络");
}
else if (str.StartsWith("11110"))
{
LbAdd("该ip属于E类网络");
}
else
{
LbAdd("ip地址计算错误!");
}
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
}
/*以下代码为清空按钮中的内容,作用是将各
* 个输入框中的内容清空
*/
private void button2_Click(object sender, EventArgs e)
{
this.txtChild.Text = "";
this.txtIp.Text = "";
this.txtYanma.Text = "";
}
}
}
第三章 结果分析与总结
3.1结果与分析
程序截图:
运行时截图:
分析:
Ip地址202.113.16.10转化为二进制为11001010011100010001000000001010
子网掩码255.255.255.0转化为二进制位11111111111111111111111100000000
他们相与之后为11001010011100010001000000000000化为点分十进制为
202.113.16.0
程序计算的结果符合结果,正确。
分析:
当ip地址用点分十进制表示时,如234.213.12.11 。由于输入的ip地址为168.113.2022.0,不符合要求所以,程序提示出错。
分析:
当ip地址用点分十进制表示时,应该为4字节,每个字节表示的范围在
0-255之间,而且每个字节之间用点号分隔。由于输入的ip地址为168.113.20没有4字节,所以程序提示出错。
分析:
Ip地址只能由数字组成,而输入的ip地址中出现了非数字的字符f,所以程序提示出错。
3.2总结
1.遇到的问题
本次我们采用c#语言作为工具,来书写我们的代码,设计了一个windows下的窗体应用程序。在此次设计中对关于ip的合法性的检测和对ip的地址与掩码相与运算上出现了些许问题。在ip地址的合法性判断上我们采用正则表达式匹配字符的方法,这是种简洁快速的判定方法。再次就是如何把用户输入的ip地址(字符串类型)转化为二进制的32位数,来和已知的掩码做与运算。在这个问题上我们想到了三种方法。
方法一:
我们把用户输入的ip地址(字符串类型)先转化成整型数值,然后与掩码相与得到子网号,但是这中方法会出现转化为整型的ip地址的大小超出了32位整型数的范围,所以我们放弃了这种方法。
方法二:
把用户输入的ip地址(字符串类型),先分别分成4个部分,存入在一个数组中,同样我们把子网掩码也做同样的处理存入一个数组。然后我们分别把这两个数组相同位序的数分别相与得到相与后的结果再存入另一个数组,最后把这个数组转化为一个字符串就得到了我们想要的ip地址与子网掩码相与后的子网号了。这种方法克服了第一种方法的类型长度不匹配的情况,是可以在程序中实现的。
方法三:
把用户输入的ip地址(字符串类型),先通过c#内置的方法IpAddress.Parse转化为字节数组,然后再用BitConverter把字节数组转化32位无符号的二进制数,子网掩码也做同样的处理,然后即可以相与的到我们想要的结果的。方法三使用了c#语言内置的函数,使得代码更加的高效,简洁,这也是方法三优越方法二的地方,所以我们就采用了方法三。
2.程序的改进
该程序参考了课程设计指导书上的部分内容,同时也对其进行了部分改进,由于书上的代码是用C++编写,同时是在dos界面下进行操作,缺乏良好的交互性,并且缺乏ip地址分类信息的判断功能,我们在程序设计中采用了用户交互性更好的窗体模式,同时增加了这一功能。
3.缺陷与不足
由于时间限制以及技术水平的局限,本程序还存在一些不足,也有一些未知bug尚未解决,但在这次课程设计中,还是获得了很多经验 。
3.2参考文献
[1] 谢希仁 计算机网络(第五版)北京:电子工业出版社2008.1
[2] 吴功宜 胡晓英 张仁 何云 王宁 计算机网络课程设计 北京:机械工业出版社 2005.9