华南农业大学课程设计
课程名称:计算机网络
项目名称:IP地址及其子网分割
开课年级:*
开设时间:*
指导教师:*
辅助指导教师:*
学生姓名:*
学 号:*
评语成绩:
IP地址及其子网分割
摘要:利用该系统实现IP 地址的判断以及其子网的分割。介绍了IP地址原理以及子网划分原理,并从总体上及功能上给予设计,再详细通过编码实现。并介绍了自己的部分心得。
关键字:IP地址,子网,子网号,掩码,划分
1.引言:IP地址的功用
正如在现实生活中给别人写信得首先确定对方的收信地址那样,网络中的两台计算机(确切地说是两个节点——node)间要相互通信必须事先知道对方节点的地址,在INTERNET上,这种标识网络节点的地址信息被称为IP地址(IP ADDRESS),通常简称为IP。INTERNET上的计算机正是通过这个具有唯一性的网络标识来识别其它的计算机,从而实现信息传输。利用该系统实现IP 地址的判断以及其子网的分割。
2.总体设计
2.1系统设计及基本知识说明
A.IP地址的组成及其分类
1、管理机构:为了确保IP地址的不重复性,客观上需要一个机构来统一管理,这个机构就是IANA(Internet Assigned Number Authority 国际号码分配局),她和各个区域的NIC(Network Information Center 网络信息中心)一起,统一分配全球范围内的IP地址,并且研究、接纳新的IP地址方案,以保证Internet飞速发展的需要。
2、组成:现行的IP地址系统是IPv4,即IP地址第4版,它以32位二进制数来表示Internet上的节点的地址,并照一定的规律划分为网络ID(netid)和主机ID(hostid)两部分。netid标识同一个物理网络上的所有节点,hostid标识在这个物理网络上的一个节点。
3、子网掩码:在TCP/IP网络上每一个节点都需要一个32位的子网掩码(Subnet Mask),它用于屏蔽IP地址的一部份,使得网络协议(TCP/IP协议组)能够区别netid和hostid。当TCP/IP节点间进行通信时,子网掩码用于判断一个节点是在本地网络还是远程网络上,以确定是否经路由转发。如果子网掩码无效,则会导致不必要的广播,从而产生广播风暴而降低系统性能,且可能导致主机的挂起。
4、表示方式:由于32位二进制数的难于记忆,因此通常将其分为四组,每组八位,中间用实心小圆点“.”隔开,并把每组中的八位二进制数转换成十进制数,形如:W.X.Y.Z的格式,这种标式称为间断十进制计数(dotted decimal notation)。以下是一个合法的IP地址:11001010110011100000000100000001(二进制格式)其对应的间断十进制格式为:202.206.1.1。
5、分类:IANA把32位的IP地址划分为A类、B类、C类、D类、E类等五类地址。其中A类地址被分配给大型网络使用,地址的最高位为0,随后7位为netid,后24位为hostid,其IP地址范围从1.0.0.1至126.255.255.254;B类地址被分配给中型网络使用,地址的最高两位为10,随后14位为netid,后16位为hostid,其IP地址范围从128.0.0.1至191.255.255.254;C类地址被分配给小型网络使用,地址的最高三位为110,随后21位为netid,后8位为hostid,其IP地址范围从192.0.0.1至223.255.255.254;D类地址通常用于多点传送给多个网络节点;E类地址用于试验性网络,保留为以后使用。除此之外,还有些特殊地规定,如:在一个netid下,hostid全为0的规定为本地地址,hostid全为1则用于广播(broadcast);A类地址中的127.0.0.0用于回送(looplack)功能,其作用是在局部计算机上试验进程间的通信等等。(关于各类网络可用的最大网络数及每个网络所支持的最大主机数请参看表一。)
网络类别 | 网络数 | 每个网络所支持的最大主机数 |
A类 | 127 | 16777214 |
B类 | 16384 | 65534 |
C类 | 2097152 | 254 |
表一:各类网络可用的最大网络数及每个网络所支持的最大主机数
B.IP地址的子网分割 1、子网分割的优点:每一个IP地址指定唯一的一台主机,而一台主机可以有不只一个IP地址,例如路由器(Route),通常具有多个节点,每个节点对应一个IP。由于IP地址由netid和hostid组成,网关(Gateway)可以很容易地从32位地址的netid域中提取网络标识符,并以此netid为基准选择路由,即网关只需知道其它网络的位置而不需要知道INTERNET上每个主机的位置,从而大大减轻路由器的负担。
众所周知,INTERNET是各种不同的网络通过共同的通信协议(TCP/IP协议组)连接而成的,在一个netid下,通常包含多个不同类型的网络(如以太网和令牌环网),再加上每个网络所支持的主机数都有一定的限制,因此有必要把同一个netid下的网络分割成多个子网(Subnet),子网间的通信不用经过网关,从而提高网络传输的效率。具有相同netid的网络经过分割,其IP地址变成由netid、subnetid、hostid三部分组成。
2、子网分割的过程:我们以一个C类网络202.206.1.Z为例来说明子网分割的过程。在此IP地址中,前3个八位组为netid,Z为hostid。当从左至右取1位子网掩码位时,IP地址开如:11001010,11001110,00000001,1???????,即分成两个子网,每个子网所支持的最大主机数为128个,其子网掩码为:255.255.255.128;当取2位掩码位时,分成四个子网,每个子网所支持的最大主机数为64个,其子网掩码为:255.255.255.192……当取7位掩码位时,分成128个子网,每个子网所支持的最大主机数为2个,其子网掩码为:255.255.255.254。
3、子网分割的地址损失:一个完整的IP地址段经子网分割后,按规定第一以及最后一段为不可用,中间各段的起始地址和终了地址也不可用。如此一来,当分割成段数最小(1个掩码位)及最大(7个掩码位)都是无意义的,因为其IP地址全部无效,而在此之间根据掩码位数的不同而有不同的地址损失(C类IP经分割后地址的损失见表二)。在实际的应用中应根据需要设定子网掩码,既要减少IP地址的损失,又得考虑子网的个数和每个子网中的有效的地址数,而且还得考虑网络进一步升级的需要。
掩码位 | 子网掩码 | 可用子 网号 | 前后两段子网地址损失数 | 每段子网最大主机数 | 中间段地 址损失数 | 地址损 失总数 |
1 | 202.206.1.128 | 无效 | 无效 | 无效 | 无效 | 256 |
2 | 202.206.1.192 | 2-3 | 128 | 64 | 4 | 132 |
3 | 202.206.1.224 | 2-7 | 64 | 32 | 12 | 76 |
4 | 202.206.1.240 | 2-15 | 32 | 16 | 28 | 60 |
5 | 202.206.1.248 | 2-31 | 16 | 8 | 60 | 76 |
6 | 202.206.1.252 | 2-63 | 8 | 4 | 124 | 132 |
7 | 202.206.1.254 | 无效 | 无效 | 无效 | 无效 | 256 |
表二:C类IP地址子网分割的地址损失
4、两个IP地址是否在同一个网络的判定:前面讲过网络上每个节点对应一个IP地址,每个IP地址都要有子网掩码。利用IP地址及子网掩码,网关能很容易地判断宿主机和目的主机是否在同一个网段上以决定是否IP转发。当网络路由接收到宿主机发出的数据报时,它把源地址与其子网掩码进行“与”运算,再把目的地址与其子网掩码进行与运算,两次运算的结果如果相同,则说明宿主机和目的主机都在当前网络上,不经转发;否则,根据netid进行转发。以202.206.1.2为例,(假设其子网掩码为:255.255.255.192)它分别向202.206.1.3和255.255.255.254发送数据报,路由运算的结果如下:
宿主机 目的主机一
IP地址 11001010110011100000000100000010;11001010110011100000000100000011
子网掩码 11111111111111111111111111000000;11111111111111111111111111000000
运算结果 11001010110011100000000100000000;11001010110011100000000100000000
十进制 202.206.1.0 202.206.1.0
目的主机二
IP地址 11001010110011100000000111111110
子网掩码 11111111111111111111111111000000
运算结果 11001010110011100000000111000000
十进制 202.206.1.192
由此可见,宿主机与目的主机一在同一网段上,而与目的主机二不在同一网段,需要转发。
2.2功能设计
1.子网号合法性判断
2.子网掩码合法性判断
3.IP地址合法性判断
4.该IP地址是否属于该子网
2.3 平台设计
此系统在c++平台上实现。
2.4 数据结构的设计
//类结构
class ipTest{
char ip[15]; //定义ip数组,存放ip地址
char subnetPlusMask[18]; //定义subnetPlusMask数组,存放子网号和子网掩码中1的个数
char subnet[18]; //定义subnet数组,存放子网号
char TempIp[15]; //定义TempIp数组,存放临时的ip地址
char TempSub[18]; //定义TempSub数组,存放临时的子网号
int mask; //定义整型mask,存放子网掩码中1的个数
bool maskIsValid;//定义布尔值maskIsValid,存放判断mask真值的变量
public:
ipTest(char *,char *); //定义ipTest函数
~ipTest(){};
bool NoIllegalChar(char *); //非法字符的判断
bool ipIsValid(char *); //判断IP地址是否合法
bool subnetIsValid(char *); //判断子网号是否合法
void belong(); //判断IP是否为子网成员
print();//定义输出函数print
};
3 详细设计
3.1 具体功能设计
1)命令格式; ip_test subnet/mask ip_addr其中ip_test 为程序名subnet为子网号 mask是一个数值ip_addr要测试的地址
2)判断subnet和ip_addr 的合法性
3)判断掩码的合法性
4)在ip地址合法的前提下,判断ip_addr是否属于子网subnet
5)输出命令中的ip是否合法,演马是否合法以及ip_a
3.2 输入格式
命令行格式:ip_test subnet/mask ip_addr
ip_test 程序名 subnet 子网号 mask 一个数值,为子网掩码1的个数 ip_addr是要测试的IP地址
例如:ip_test 202.113.16.0/24 202.113.16.10
3.3 流程图
Y Y
N
N
N Y
Y N
N
Y
Y
N
N
Y
3.4 详细代码
#include <iostream.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <ctype.h>
//类结构
class ipTest{
char ip[15]; //定义ip数组,存放ip地址
char subnetPlusMask[18]; //定义subnetPlusMask数组,存放子网号和子网掩码中1的个数
char subnet[18]; //定义subnet数组,存放子网号
char TempIp[15]; //定义TempIp数组,存放临时的ip地址
char TempSub[18]; //定义TempSub数组,存放临时的子网号
int mask; //定义整型mask,存放子网掩码中1的个数
bool maskIsValid;//定义布尔值maskIsValid,存放判断mask真值的变量
public:
ipTest(char *,char *); //定义ipTest函数
~ipTest(){};
bool NoIllegalChar(char *); //非法字符的判断
bool ipIsValid(char *); //判断IP地址是否合法
bool subnetIsValid(char *); //判断子网号是否合法
void belong(); //判断IP是否为子网成员
print();//定义输出函数print
};
//主函数
void main(int argc,char *argv[]){
int i;
printf("您输入了 %d 个参数\n分别是:",argc);
for(i=0;i<argc;i++)
printf("%s\n",argv[i]);
if(argc != 3){ //判断参数格式是否正确
cout <<"error" <<endl <<"format shoud be:ip_test subnet/mask ip" <<endl;
return;
}
else{
if(strlen(argv[1]) > 18){ //先判断最简单的错误,子网号及子网掩码长度和是否超出
cout <<"subnet/mask is too long";
return;
}
if(strlen(argv[2]) > 15){ //ip地址长度是否超出
cout <<"ip is too long";
return;
}
ipTest test(argv[1],argv[2]); //实例化ipTest类
test.print(); //完成相应判别并输出结果
}
}
//构造函数
ipTest::ipTest(char* subnetPlusMask,char *ip){
maskIsValid = true;
char temp[2];
//把'/'前的字符复制到subnet字符数组中
int smLen = strlen(subnetPlusMask); // 定义smLen为subnetPlusMask的长度
for(int i = 0;i < smLen && subnetPlusMask[i] != '/';i++){
this->subnet[i] = subnetPlusMask[i]; //对subnet数组赋值,值为subnetPlusMask中的数值
this->TempSub[i] = subnetPlusMask[i]; //对TempSub数组赋值,值为subnetPlusMask中的数值
}
subnet[i] = '\0';//给数组加上结束符
TempSub[i] = '\0'; //给数组加上结束符
if(i >= smLen-3){ //初始化mask和maskIsValid
if(i == smLen-2){
temp[0] = subnetPlusMask[i+1];
if(!isdigit(temp[0])) //判断temp中的值是否为字符
maskIsValid = false;
}
else if(i == smLen-3){
temp[0] = subnetPlusMask[i+1];
temp[1] = subnetPlusMask[i+2];
if(!(isdigit(temp[0])&&isdigit(temp[1]))) //判断temp中的值是否为字符
maskIsValid = false;
}
mask = atoi(temp); //将temp中的字符数值化
if(mask <0 || mask >32) //判断mask值是否超出范围
maskIsValid = false;
}
else //用十进制数表示的掩码中1的个数只能是一位数或者两位数
maskIsValid = false;
strcpy(this->subnetPlusMask,subnetPlusMask); //给subnetPlusMask赋值
strcpy(this->ip,ip); //给IP赋值
strcpy(this->TempIp,ip);
}
//调用判别函数,并输出结果
ipTest::print(){
bool subIsV = subnetIsValid(TempSub);
bool ipIsV = ipIsValid(TempIp);
if(!subIsV) //subnet非法
cout <<"subnet is invalid!" <<endl;
else
cout <<"valid subnet:" <<subnet <<endl;
if(!maskIsValid) //mask非法
cout <<"mask is invalid!" <<endl;
else
cout <<"valid mask:" <<mask <<endl;
if(!ipIsV) //ip非法
cout <<"ip is invalid!" <<endl;
else
cout <<"valid ip:" <<ip <<endl;
//判断ip是否belong subnet
if(subIsV && ipIsV && maskIsValid)
belong();
}
//子网数,判断输入是否含有非数字字符
bool ipTest::NoIllegalChar(char *ch){
unsigned int i,k=0;
for(i=0;i <strlen(ch);i++){
if(isdigit(*(ch+i))==0){ //判断每一位是否为数字字符
return false;
}
}
return true; //若不含有非法数字字符则返回true
}
//判别IP地址是否合法
bool ipTest::ipIsValid(char * ip){
char ch[]="."; //分隔符
char *token,*dot[4];
int iplen = 0;
token = strtok(ip,ch); //以"."标志将IP字符串按节分开
while(token!=NULL){ //循环进行,直到结束
dot[iplen]=token; //将分开的每段赋值给dot
iplen++;
token = strtok(NULL,ch);
}
if(iplen != 4) //段数不对
return false;
for(int i = 0; i < 4; i++){
if(!NoIllegalChar(dot[i])||atoi(dot[i])>255) //有非法字符或某段值非法
return false;
}
return true;
}
//判断子网号是否合法
bool ipTest::subnetIsValid(char* subnet){
if(!ipIsValid(subnet)) //调用判别IP地址合法性的函数
return false;
return true;
}
//判断IP是否为子网成员,判断子网号与掩码是否匹配,以及子网号、主机号全0全1问题
void ipTest::belong(){
int subLen = strlen(subnet);
int ipLen = strlen(ip);
unsigned int iIPA,iSubA,iMask;
unsigned char subA[4],ipA[4];
char temp[3];
int i,j,t = 0;
for (i = 0, j= 0;i <subLen;i++){ //不用再检错
if(subnet[i] !='.')
temp[j++]=subnet[i]; //temp数组中放'.'间的串
else{
subA[3-t] = atoi(temp); //subA数组中放'.'间的数据
j=0;
t++;
temp[0]=temp[1]=temp[2]='\0';
}
}
subA[0]=atoi(temp);
temp[0]=temp[1]=temp[2]='\0';
iSubA=*(unsigned int *)subA; //iSubA中放subnet中'.'除外的串对应的数
for(i=0,j=0,t=0;i <ipLen;i++){ //不用再检错
if(ip[i]!='.')
temp[j++]=ip[i];
else{
ipA[3-t]=atoi(temp);
j=0;
t++;
temp[0]=temp[1]=temp[2]='\0';
}
}
ipA[0]=atoi(temp);
iIPA=*(unsigned int*)ipA; //iIPA中放IP中'.'除外的串对应的数
iMask = 0xffffffff;
iMask <<=(32-mask);
//获得掩码
if((iSubA | iMask)!= iMask){ //说明sub与mask不匹配
cout <<"子网号与掩码不匹配,error!" <<endl;
return;
}
if((iSubA^iMask)==0){ //说明子网号全1
cout <<"子网号全1,error!" <<endl;
return;
}
if((iSubA&iMask)==0){ //说明子网号全0
cout <<"子网号全0,error!" <<endl;
return;
}
if((iSubA)==(iIPA&iMask)){ //IP和掩码与,结果和子网号比较
if((iIPA|iMask)==iMask){ //说明主机号全0
cout <<"主机号全0,error!" <<endl;
return;
}
if((iIPA|iMask)==0xffffffff){ //说明主机号全1
cout <<"主机号全1,error!" <<endl;
return;
}
//ip属于subnet
cout <<" " <<ip <<" belongs to " <<subnetPlusMask <<endl;
return;
}
//ip不属于subnet
else
cout <<" " <<ip <<" doesn't belong to " <<subnetPlusMask <<endl;
}
4 总结
1.相对而言这个实验比较简单,只是判断ip地址,子网号,子网掩码的合法性以及属不属于该子网。功能实现起来也并不复杂,就是要考虑周全一点,像ip不能全为1或为0的情况,像掩码1的个数不能超过32等等,排除了这些情况之后,基本ok。调试也没有花很久的时间。
2.通过这个实验,自己动手完成了ip的判断操作等,对ip分类以及子网划分这一块熟悉了一下,印证了之前自己的理解。
3.网上还是有很多可以参考的。像里面关于IP知识的介绍大多便是在网上参考的。
【参考文献】
谢希仁《计算机网络教程(第2版)》 人民邮电出版社
张钟澎 《中小型办公网络组网技术》 电子科大出版社
王相林《组网技术与配置(第2版)》 清华大学出版社
【小组自评】
B+