课程设计:c语言解决ip地址合法性和子网掩码的判断

大连民族大学
计算机科学与工程学院课程设计报告
课程设计名称: 程序设计基础课程设计
题目名称: IP地址合法性及子网判断程序
完成时间: 2020.03.02 - 2020.04.05
专业: 网络工程
班级: 192
学号: 2019083221
成绩:

需求分析
根据任务要求,该系统可以实现IP地址合法性和子网的判断,相应的函数应该能够判断点分十进制+子网掩码和CIDR网络地址是否合法,并输出子网容量和子网掩码等相关的网络信息。还要能够对两个IPV4地址是否属于同一个子网进行判断。
输入:程序提供菜单系统,用户根据相应的各级菜单进行选择,可以实现点分十进制+子网掩码的判断,CIDR网络地址的判断,两个IPV4地址是否属于同一个子网内。并经输入信息保存了文件中。
输出/结果:从输出和信息中获得信息,并根据相应的函数输出相关的网络信息。
概要设计:
在这里插入图片描述

详细设计:

功能:保存31正确的子网掩码
参数说明

Char rightmask[31][16] = {"0.0.0.0","128.0.0.0","192.0.0.0","224.0.0.0","240.0.0.0","248.0.0.0","252.0.0.0","254.0.0.0","255.0.0.0""255.128.0.0","255.192.0.0","255.224.0.0","255.240.0","255.248.0.0","255.252.0.0","255.254.0.0","255.255.0.0","255.255.128.0","255.255.192.0","255.255.224.0","255.255.240.0","255.255.248.0","255.255.252.0","255.255.254.0","255.255.255.0","255.255.255.128","255.255.255.192","255.255.255.224","255.255.255.240","255.255.255.248","255.255.255.252" };

ringmask[31][16]:全局数组,二维数组保存全部合法的子网掩码。

功能:判断子网掩码是否合法
参数说明:
int Subnet_Mask_judge(char* subnet)
Subnet:输入参数,数组类型指针,指向需要判断的子网掩码字符数组。
返回值:判断正确返回1,判断错误返回1。
算法:
if(IP_address_judge(subnet) != 0)
保证掩码必须是一个合法的IPv4地址
if(strcmp(subnet,rightmask[i])==0)
与rightmask比较是否有相同的,相同则掩码合法,反之不合法。

功能:判断IPV4地址是否合法。
参数说明:
StrIp:输入参数,数组类型指针,指向需要判断的ip字符数组。
返回值:-1或0。
算法:

功能:判断CIDR地址是否合法。
参数说明:
CIDR_judge(char CIDR)
CIDR:输入参数,字符串类型指针,指向需要输入的CIDR字符串。
返回值:判断正确返回0,错误返回-1。
算法:
for(i=0; CIDR[i]!=’/’; i++) //判断条件为不等于’/’
{
strCIDR[i]=CIDR[i];
}
截取存储CIDR前面的ip地址
i++; // 跳过’/’
for(; CIDR[i]!=’\0’; i++) //保存 前缀
{
sum=10
sum +CIDR[i]-‘0’;
}
截取存储CIDR的前缀
if(IP_address_judge(strCIDR) ==0)
if(sum >=0 &&sum <= 32 )
判断ip和前缀是否合法

功能:将掩码转化为前缀形式。
参数说明:
Mask_Prefix_change(char* mask)
Mask:输入参数,用来存储需要转换的掩码。
返回值:返回前缀的值。
算法:
for (i = 0; i<31; ++i)
控制循环在rightmask中检索。
if (strcmp(mask, rightmask[i]) == 0)
return i;i的值就是前缀长度。

功能:前缀转化为掩码
参数说明:
Prefix_Mask_change(int n)
n:输入参数,用来传递前缀的值。
返回值:返回掩码的值。
算法:
if (n >= 0 && n <= 31)
return rightmask[n];
else
return “Error Prefix”;

功能:计算子网容量
参数说明:
Mask_capacity (int n)
n: 输入参数,用来传递前缀的值。
返回值:子网容量的大小,应该减去网络地址和广播地址。
算法:
for(i=0;i<(31-n);i++)
{
sum *=2;
}
sum = sum - 2;

功能:根据输入的IP地址和掩码MASK求子网地址
参数说明:
char* Subnet_one(char *IP,char *MASK)
IP:输入参数,用来传递的点分十进制ip地址的值。
MASK:输入参数,用来传递子网掩码的值。
返回值:子网地址的值。
算法:
sscanf(IP, “%u.%u.%u.%u”, &IP_n[3], &IP_n[2], &IP_n[1], &IP_n[0]);
sscanf(MASK, “%u.%u.%u.%u”, &MASK_n[3], &MASK_n[2], &MASK_n[1], &MASK_n[0]);
sprintf(SUBNET, “%u.%u.%u.%u”, IP_n[3] & MASK_n[3], IP_n[2] & MASK_n[2], IP_n[1] & MASK_n[1], IP_n[0] & MASK_n[0]);
主要利用sscanf函数和sprintf函数读取和储存形如255.255.255.255格式的子网字符串,并输出。

功能:判断并保存点分十进制+子网掩码
参数说明:
Input_ip:字符型数组,用来保存输入的ip地址。
Input_subnet:字符型数组,用来保存输入的子网掩码地址。
返回值:无。
算法:
FILE *fp= fopen(“ip.txt”, “w”); 创建一个ip.txt文件,权限为写。
fp = fopen(“ip.txt”, “w”)) == NULL;利用if语句判断文件是否正常打开正常打开继续程序,反之报错。
IP_address_judge(Input_ip) == 0; 判断IP地址是否合法,合法程序继续运行,反之利用while语句重新输入IP地址。
fprintf(fp,"%s",Input_ip);将IP地址存储到文件中。
Subnet_Mask_judge(Input_subnet) == 1;
fprintf(fp," %s\n",Input_subnet);与IP地址方法同理。

功能:判断并保存CIDR。
参数说明:
Input_CIDR:字符型变量,用来保存输入的CIDR地址。
返回值:无。
算法:
fpp = fopen(“CIDR.txt”, “w”)) == NULL;打开CIDR.txt文件并利用if语句判断文件是否正常打开。正常打开文件,反之报错。
CIDR_judge(Input_CIDR) ==0;
fprintf(fpp,"%s\n",Input_CIDR);判断CIDR格式是否正确,如果正确就写入到文件中,反之就利用while循环重新输入CIDR地址。

功能:输出点分十进制+子网掩码相关信息。
参数说明:
get_ip[16]:字符型数组,用来在文件中读出ip地址。
get_subnet[16]:字符型数组,用来在文件中读取子网掩码。
Mask_address[16] :字符型数组,用来保存自定义函数中返回的子网掩码地址。
Prefix:整型变量,用来储存子网转换的前缀长度。
返回值:无。
算法:
FILE *fp= fopen(“ip.txt”, “r”); 打开文件,并给予读权限。
fscanf(fp,"%s %s",get_ip,get_subnet);读取ip和掩码的值。
prefix = Mask_Prefix_change(get_subnet);将掩码转化为前缀长度
Mask_capacity (prefix);利用转化的前缀长度,计算子网容量。
Subnet_one(get_ip,get_subnet);计算子网地址。

功能:输出CIDR的相关信息。
参数说明:
get_CIDR[20] :字符型数组,用来在文件中读取CIDR地址。
get_CIDR_up [16] :字符型数组,用来保存CIDR中的ip地址。
get_CIDR_down = 0:无符号整形变量,用来保存前缀大小。
IP_m[4]:无符号字符变量,用来保存ip地址的四段数字。
返回值:无。
算法:
FILE *fpp= fopen(“CIDR.txt”, “r”);打开文件,并给予读权限
fscanf(fpp,"%s",get_CIDR);读取文件CIDR地址。
sscanf(get_CIDR,"%u.%u.%u.%u/%u",&IP_m[0],&IP_m[1],&IP_m[2],&IP_m[3],&get_CIDR_down); 将CIDR地址分段保存。
sprintf(get_CIDR_up,"%u.%u.%u.%u",&IP_m[0],&IP_m[1],&IP_m[2],&IP_m[3]);
把分段保存的CIDR地址中一部分组成ip地址。然后和点分十进制+子网掩码的方法一样,利用自定义函数计算子网容量和子网地址。

功能:两个ip地址是否属于同一个子网输出。
参数说明:
ip[2][16]:字符型数组,用来保存键盘输入的两个ip地址。
mask[2][16]:字符型数组,用来保存键盘输入的两个子网掩码。
tool[2][16]::字符型数组,用来接受函数的返回值。
返回值:无。
参数说明:
IP_address_judge(ip[0]) = 0 && IP_address_judge(ip[1]) = 0
Subnet_Mask_judge(mask[0]) =1&&Subnet_Mask_judge(mask[1]) = 1
strcpy(tool[0],Subnet_one(ip[0],mask[0]));
strcpy(tool[1],Subnet_one(ip[1],mask[1]));
首先判断ip地址是否合法,不合法重新输入,如果合法,判断子网掩码是否合法同理。

使用说明:
在这里插入图片描述
打开程序后,弹出选择菜单栏。
在这里插入图片描述
需要继续输入ip地址,ip地址正确后继续输入子网掩码,均输入正确后给出子网容量和子网地址,并继续弹出菜单。
在这里插入图片描述
如果ip地址输入错误,会对相关信息报错,并重新输入。
在这里插入图片描述
如果子网掩码输入错误,会对相关信息报错,并重新输入。
在这里插入图片描述
需要继续输入CIDR地址,CIDR地址正确后继续输入子网掩码,均输入正确后给出子网容量和子网地址,并继续弹出菜单。
在这里插入图片描述
对错误的CIDR进行的判断并重新输入。
在这里插入图片描述
选择选项3,需要按步骤输入第一个ip地址,第二个地址。再输入两个子网掩码进行判断是否属于同一个子网。
在这里插入图片描述
如果输入错误也会对相关错误进行报错,并重新输入。
在这里插入图片描述
选项4,退出程序。
在这里插入图片描述
心得体会:
长达三周的课程设计终于告一段落,在这个活动中我真的收获了很多的知识,提升了自己的能力。说实话,长达两个月的寒假,没有复习c语言,真的忘记了很多很多的知识,连最基本的do-while语句都忘记了,但是我没有被困难打到。第一周我没有进行代码的编写,我选择的是第二题“ip地址合法性和子网掩码判断”,这需要对IPV4地址和CIDR地址相关的知识有了解,因此在第一周我选择去复习c语言的知识,并在B站上面学习了IPV4地址和CIDR地址的相关信息。第二周开始着手对代码进行编写,一开始对IPV4地址合法性进行判断的函数遇到了很大障碍,总是对错误有遗漏,要不就是对相关的判断冲突。后来,我冷静下来对IPV4的错误六大错误进行整理,梳理它们的内在联系。最终以“.”这个字符为标志进行判断解决问题。从中我懂得了,我们遇到问题一定不能着急,心平气和的去梳理内在联系,寻求解决问题的方法。第三周开始我开始优化的代码,如一个二维数组在三个自定义的文件中都需要使用就把它定义为一个全局变量,成功的简化代码,避免数据的重复写入。还优化了主函数从130多行变成了30行,从中我懂得,主函数不要写过多的东西一简洁明了为主,这样的话主函数相当于一个目录,大大提高了程序的可读性。我在优化的过程中,还使用了对文件的操作,fopen()、fclose()、fprintf()、fscanf()、fgetc()、fput()等。还有文件的权限‘r’、‘w’、‘a’等。在我使用文件操作的时候,出现了一个问题,文件写入后显示写入成功,但是读取的信息完全错误,打开相应的txt文件发现文件里没有信息。找了一整个上午都没有发现错误,只能求助老师,在王德高老师的帮助下,发现是一个文件读取函数中的读权限给成了写权限,导致的错误,因为自己的不细心浪费了很长时间,在编写代码的时候一定要细心有时候敲错一个字母,程序都可能无法正常运行。
课程设计为我们提供了一个把理论进行实践的绝好机会,课程设计要求我们既动手又动脑,断粮我们的分析实际问题,解决实际问题的能力,同时也提高我们适应实际,实践的能力。
最后,在这里感谢第二题的指导教师王德高老师,在群里给我们讲解ip的相关知识,解答学生问题的时候,特别的耐心,感谢王德高老师。

附录:课程设计源代码。
#include <stdio.h>
#include <string.h>
char CIDR_up[20] = {0};
int  CIDR_down;

char rightmask[31][16] = {"0.0.0.0","128.0.0.0","192.0.0.0","224.0.0.0","240.0.0.0","248.0.0.0","252.0.0.0","254.0.0.0","255.0.0.0",
	                          "255.128.0.0","255.192.0.0","255.224.0.0","255.240.0","255.248.0.0","255.252.0.0","255.254.0.0","255.255.0.0",
	                          "255.255.128.0","255.255.192.0","255.255.224.0","255.255.240.0","255.255.248.0","255.255.252.0","255.255.254.0","255.255.255.0",
	                          "255.255.255.128","255.255.255.192","255.255.255.224","255.255.255.240","255.255.255.248","255.255.255.252"
	                         };

//判断IPv4地址是否合法
int IP_address_judge(char *strIp)
{
	
	int num = 0, numcount=0;      // num为每段的十进制数,numcount为每段的位数
	int dotNum = 0;		         //dotNum为'.'的个数
	do
	{
		if((*strIp == '0') )   //字段中的数字小于10,且不能以0开头
		{ 
				if(*(strIp+1) != '.'&&*(strIp+1) != '\0')
				{	
				   if(*(strIp-1) == '.')
				   {   
						printf("当字段中的数字小于10,不能以0开头\n");
					    return -1;
					}
					
				}
			}
		if(*strIp >= '0' && *strIp <= '9')
		{
				num = 10 * num + *strIp - '0';
				numcount++;
				if (numcount>=4) //每段位数 numcount 最大为3位
				{ 
					printf("IPv4地址的每一段最大为3位数,您输入了 %d 位数\n",numcount);
					return -1;
				}
				
			} 
		else if(( *strIp == '.') || (*strIp) == '\0' ) 
		{
			//如果包含'..' 或 以 '.'结尾
			if( *(strIp - 1) == '.' || (*(strIp-1)) == '\0' ) 
			{
				printf("IPv4地址不能包含'..' 或 以 '.'开头或结尾\n");
				return -1;
			}
			//每段的数字num> 255 或 num< 0
			if(num < 0 || num > 255)
			{
				printf("IPv4地址每段的数值必须在0~255之间\n");
				return -1;
				}
			num = 0;
			if(*strIp == '.') // 若是正常的 '.'
			{	
				dotNum++;	 	//记录正常的'.'的个数
				numcount=0;		//每段的位数计数器 numcount 归零
			}
		}
		 else //包含非法字符
		{	
			printf("IPv4地址包含非法字符\n");
			return -1;
		 	}
	 } while(*(strIp++));

	if( dotNum == 3 ) 
	{
		// 输入正确
		return 0;
		}
	else 
	{
		//'.'的个位不为3
		printf("IPv4地址中的'.'不为3\n");
		return -1;
		}
	
 }

//判断子网掩码是否合法
int Subnet_Mask_judge(char* subnet)
{

	if(IP_address_judge(subnet) != 0) //掩码必须是一个合法的IPv4地址
	{	
		return 0;
	}
    int i = 0; 
	for(i=0; i<31; i++ ) 
	{
		if(strcmp(subnet,rightmask[i])==0) 
		{
			return 1;
		}
	}

	return 0;
}

//CDIR是否合法判断
int CIDR_judge(char *CIDR)
{   
   int i,sum = 0;
  char strCIDR[16];
  if(strstr(CIDR,"/") == NULL) 
  {
  	printf("CIDR地址输入不正确!\n");
  	return -1;
  }
  for(i=0; CIDR[i]!='/'; i++)	//判断条件为不等于'/' 保留前面的ip 
	{
		strCIDR[i]=CIDR[i];
	}
	i++;	// 跳过'/' 
	for(; CIDR[i]!='\0'; i++)  //保存 前缀 
	{
		sum=10*sum +CIDR[i]-'0';
	}
	 if(IP_address_judge(strCIDR) ==0)
	 {
	 	if(sum >=0 &&sum <= 32 )
	 	{
	 		strcpy(CIDR_up,strCIDR);
	 		CIDR_down = sum;
	 		return 0;
		 }
		 else
		 {
		 	printf("CIDR中的前缀非法!\n"); 
		 	return -1; 
		 }
	 }
     else
     {
     	printf("CDIR中的IP地址非法!\n");
		 return -1; 
	 }
	
}

//将掩码转化为前缀形式 
int Mask_Prefix_change(char* mask) 
  {
  	int i = 0;
	
	for (i = 0; i<31; ++i) 
	{
		if (strcmp(mask, rightmask[i]) == 0) 
		{
			return i;	//rightmask[i]的下标i就是前缀长度
		}
	}
	return -1;
}

//前缀转化为掩码 
char* Prefix_Mask_change(int n)
{

	if (n >= 0 && n <= 31) 
	{
		return rightmask[n];	//rightmask[n]就是掩码
	}
	else
	{
		return "Error Prefix";
	}

 }

//子网容量运算
int Mask_capacity (int n)
{
	int sum = 2;
	int i = 0; 
	for(i=0;i<(31-n);i++)
	{
		sum *=2;
	 } 
	 sum = sum - 2;
	return sum;
 }

//根据输入的IP地址和掩码MASK求子网地址
 char* Subnet_one(char *IP,char *MASK) 
{
	unsigned int IP_n[4],MASK_n[4]; //IP_n, MASK_n分别是IP地址和掩码MASK的各段数字
	sscanf(IP, "%u.%u.%u.%u", &IP_n[3], &IP_n[2], &IP_n[1], &IP_n[0]);
	sscanf(MASK, "%u.%u.%u.%u", &MASK_n[3], &MASK_n[2], &MASK_n[1], &MASK_n[0]);

	char SUBNET[16] = "";
	sprintf(SUBNET, "%u.%u.%u.%u", IP_n[3] & MASK_n[3], IP_n[2] & MASK_n[2], IP_n[1] & MASK_n[1], IP_n[0] & MASK_n[0]);

	return SUBNET;
  }
  
//判断并保存点分十进制+子网掩码 
void write_ip()
{
FILE *fp= fopen("ip.txt", "w");//定义文件指针 
char Input_ip[16]="";
char Input_subnet[16]=""; 
if((fp = fopen("ip.txt", "w")) == NULL)
       {
       	printf("can't open the file! \n");
		} 
else
	{
		while(1)
		{
			printf("请输入IPV4地址:");
			scanf("%s",Input_ip);
			if(IP_address_judge(Input_ip) == 0) 
			{
				fprintf(fp,"%s",Input_ip);
				while(1)
				{
					printf("请输入子网掩码:");
					scanf("%s", Input_subnet);
					if(Subnet_Mask_judge(Input_subnet) == 1)
					{
					 	fprintf(fp,"    %s\n",Input_subnet);
					 	printf("IPv4地址和掩码输入正确\n");
						break;
					}
					else
					{
						printf("子网掩码输入错误。\n");
					}
				
				}	
			break;
			} 
			else 
			{
				//printf("IPv4地址输入错误!\n");
			}
		}
	}
	
	fclose(fp); 	
 } 
 
 //判断并保存CIDR 
void write_CIDR()
{
	FILE *fpp= fopen("CIDR.txt", "w");
	char Input_CIDR[20]="";
	if((fpp = fopen("CIDR.txt", "w")) == NULL)
        printf("can't open the file! \n");
	else
	{
		while(1) 
	{
		printf("请输入CIDR地址:");
		scanf("%s", Input_CIDR);
		if( CIDR_judge(Input_CIDR) ==0) 
		{
			printf("CIDR地址输入正确\n");
			fprintf(fpp,"%s\n",Input_CIDR);
			break;
			}
		else
		{
			//printf("CIDR地址输入不正确!\n");
			} 
			
		}
	}
	
	fclose(fpp);
}
//输出点分十进制+子网掩码相关信息 
void read_ip()
{
 FILE *fp= fopen("ip.txt", "r");//定义文件指针 
 char get_ip[16]="";
 char get_subnet[16]="";
 char Mask_address[16] = {0}; //储存子网地址  
 int prefix = 0;
 if((fp = fopen("ip.txt", "r")) == NULL)
       {
       	printf("can't open the file! \n");
		} 
 else
	{
		fscanf(fp,"%s    %s",get_ip,get_subnet);	
        prefix = Mask_Prefix_change(get_subnet);
		printf("子网容量为:%d\n",Mask_capacity (prefix));
		strcpy(Mask_address,Subnet_one(get_ip,get_subnet)) ;
		printf("子网网络地址为:%s\n",Mask_address);
	}
	fclose(fp);
}

//输出CIDR的相关信息 
void read_CIDR()
{
	FILE *fpp= fopen("CIDR.txt", "r");
	char get_CIDR[20] = "";
	char get_CIDR_up [16] = "";
	unsigned int get_CIDR_down = 0,IP_m[4];
	char return1[16] = "";  //用来接收函数返回值
	char return2[16] = ""; 
	if((fpp = fopen("CIDR.txt", "r")) == NULL)
       {
       	printf("can't open the file! \n");
		} 
 	else
	{
	fscanf(fpp,"%s",get_CIDR);
	sscanf(get_CIDR,"%u.%u.%u.%u/%u",&IP_m[0],&IP_m[1],&IP_m[2],&IP_m[3],&get_CIDR_down);
	sprintf(get_CIDR_up,"%u.%u.%u.%u",&IP_m[0],&IP_m[1],&IP_m[2],&IP_m[3]);
	printf("子网容量为:%d\n",Mask_capacity (get_CIDR_down));
	strcpy(return2,Prefix_Mask_change(get_CIDR_down));
	strcpy(return1,Subnet_one(CIDR_up,return2));
	printf("子网地址为:%s\n",return1);	
	}
	fclose(fpp);
}

//两个ip地址是否属于同一个子网输出
void IP_two_main() 
{
	char ip[2][16] = {""}, mask[2][16] = {""}, tool[2][16]={""}; //用来接收函数返回值 
		while(1)
		{
			printf("请输入第一个IPV4地址:");
		  	scanf("%s",ip[0]); 
			printf("请输入第二个IPV4地址:");
			scanf("%s",ip[1]);
			if(IP_address_judge(ip[0]) == 0 && IP_address_judge(ip[1]) == 0)
			{
				printf("请输入第一个IPV4地址的子网掩码:");
				scanf("%s",mask[0]);
			
				printf("请输入第二个IPV4地址的子网掩码:");
				scanf("%s",mask[1]);
				if(Subnet_Mask_judge(mask[0]) ==1&&Subnet_Mask_judge(mask[1]) == 1)
				{
					strcpy(tool[0],Subnet_one(ip[0],mask[0]));
					strcpy(tool[1],Subnet_one(ip[1],mask[1]));
					if(strcmp(tool[0],tool[1]) == 0)
					{
					  printf("两个IPV4地址属于同一子网\n"); 
					  break; 
					}
					else
					{
					  printf("两个IPV4地址不属于同一子网\n"); 
					  break;	
					}
				}
				else
				{
					printf("子网输入错误,请重新输入。\n");
				 } 
			}
			else
			{
				printf("ip地址输入错误,请从新输入。\n");
			}
		}
			
 } 
int main() 
{
	while(1)
	{
			int i=0;
			printf("**********************************************************\n");
			printf("	1.点分十进制+子网掩码判断\n");
			printf("	2.CDIR型ip地址判断\n");
			printf("	3.判断两个ip地址是否属于一个子网\n");
			printf("	4.退出程序\n");
			printf("***********************************************************\n");
			printf("请选择您需要的服务:");
			scanf("%d",&i);
			switch(i)
			{
				case 1:
					write_ip();
					read_ip();
			    	break;
				case 2:
			    	write_CIDR();
			    	read_CIDR();
			    	break;
				case 3:
			    	IP_two_main();    
			    	break;
				case 4: 
			    	printf("欢迎下次使用本程序。\n");
			    	goto over;
				default:
					printf("您输入的选项非法!\n");
					break;
		 		} 
		} 
over:		
	return 0;
}	

发布了16 篇原创文章 · 获赞 3 · 访问量 423

猜你喜欢

转载自blog.csdn.net/qq_23321269/article/details/105133498