识别有效的IP地址和掩码并进行分类统计
淅淅沥沥地先积累一些知识:
合法的子网掩码,按位取反,加一后,二进制位中,只有一个1。
IP是32位二进制数据,通常以十进制表示,并以“.”分隔。IP地址是一种逻辑地地址,用来标识网络中一个个主机,IP有唯一性,即每台机器的IP在全世界是唯一的。
电脑之间要实现网络通信,就必须要有一个合法的ip地址。IP地址=网络地址+主机地址,(又称:主机号和网络号组成)ip地址的结构使我们可以在Internet上很方便的寻址。ip地址通常用更直观的,以圆点分隔号的四个十进制数字表示,每个数字从0到255,如某一台主机的ip地址为:128.20.4.1在局域网里,同样也需要ip地址,一般内网的ip地址是以192.168开头的,这样很容易区分公网和内网的ip地址。
(收笔于此,暂去徜徉!2019-07-20 22:06:39)
网段
IP默认分配的子网掩码每段只有255或0
A类的默认子网掩码 255.0.0.0 一个子网最多可以容纳1677万多台电脑(2^24- )
B类的默认子网掩码 255.255.0.0 一个子网最多可以容纳6万台电脑(2^16- )
C类的默认子网掩码 255.255.255.0 一个子网最多可以容纳254台电脑 (2^8-2)
所有的子网掩码是由一串连续的1和一串连续的0组成的(一共4段,每段8位,一共32位数)
详见:https://blog.csdn.net/jlminghui/article/details/50518765
子网掩码决定的是一个子网的计算机数目,计算机公式是2的m次方,其中,我们可以把m看作后面0的个数。如255.255.255.0转换成二进制,那就是11111111.11111111.11111111.00000000,后面有8颗0,那m就是8,255.255.255.0这个子网掩码可以容纳2的8次方(台)电脑,也就是256台,但是有两个IP是不能用的,那就是最后一段不能为0和255,减去这两台,就是254台。
255.255.254.0 是由23个1组成,也就是23位掩码。简单的说,由左往右数1,把十进制的IP换算成二制后,前23位1相同的情况下就属于同一个网段内的IP,24位后的1随意变化,属于同一网段。例中IP(A) 188.188.0.111,通过这个23位限制,可以看出的范围就是属于 188.188.0.0~188.188.1.255范围,只有在这个范围内,才属于同一个网段内IP。IP(B)同网段的IP范围是188.188.4.0~188.188.5.255的范围,显然不在IP(A)的范围内,所以IP(A)与IP(B)不是同一网段内IP。
现在的IP暂时由四段数字组成(以后将扩充更改),常分为三类IP:
A类:0.0.0.0到126.255.255.255
B类:128.0.0.0到191.255.255.255
C类:192.0.0.0到223.255.255.255
子网掩码:
在同一网段,要求网络标识相同,网络标识就是用IP的二进制与子网掩码的二进制数据作'与'运算(可用WINDOWS计算器算二进制),所以结果相同,表示在同一网段,而不是IP地址前几段相同就表示在同一网段。算网络标识的时候,A类IP只算第一段,B类只算第一,二段,C类IP算第一,二,三段。
例:192.168.0.1 255.255.255.0的网络标识为:192.168.0.0
192.168.0.1: 11000000.10101000.00000000.00000001
255.255.255.0:11111111.11111111.11111111.00000000
作与运算:
11000000.10101000.00000000.00000000
结果:192.168.0.0
子网掩码决定了一个子网的计算机数目,简单的算法就是2的M次方。M表示二进制的子网掩码后面0的数目。
如果第一个是255,则判断第二个是不是255,若第二个不是255,则第三个和第四个必须是0,同时第二个必须是254,252,248,240,224,192,128,0;若第二个为255,则第三个以此类推。
如果第一个不是255,则同理,只能是254,252,248,240,224,192,128(注意不能为0),因为子网掩码mask可以以0开头,但不是有效的掩码。
实例:一个公司有530台电脑,组成一个对等网,子网掩码如何设?IP如何设?
子网掩码:
2的M次方=530,求得M=10 (此处我尚有疑惑! 疑惑于黄昏得解:
首先,无疑,530台电脑用B类IP最合适(A类不用说了,太多,C类又不够,肯定是B类),但是B类默认的子网掩码是255.255.0.0,可以容纳6万台电脑,显然不太合适,那子网掩码设多少合适呢?我们先来列个公式。
2的m次方=560
首先,我们确定2一定是大于8次方的,因为我们知道2的8次方是256,也就是C类IP的最大容纳电脑的数目,我们从9次方一个一个试2的9次方是 512,不到560,2的10次方是1024,看来2的10次方最合适了。)
那么子网掩码最后为10个0,如此便是:11111111.11111111.11111100.00000000
换成十进制便是:255.255.252.0
再看IP,我们选一个B类IP,例如:188.188.×.×
前两段按B类要随便设就可以,关键是第三段,只要网络标识相同就可以在同一网段就可以,我们先看网络标识:
255.255.252.0:11111111.11111111.11111100.00000000
188.188.×.×: 10111100.10111100.??????××.××××××××
网络标识: 10111100.10111100.??????00.00000000
上边×号无论填0和1结果都是0
?处填0和1都一样,我们就全填0,结果便是IP便是:
10111100.10111100.000000××.××××××××,这个IP共有530台电脑,IP最后一段分给254台,一共要分530/254=2.086段,进一法则要分成3段,所以IP地址000000××处分成三个不同的数据即可,例:00000001,00000010,00000011,分别是1,2,3,这样IP地址就确定了188.188.1.×,188.188.2.×,188.188.3.×。
拾锦:
分配和计算子网掩码(Subnet mask)你会了吧,下面,我们来看看IP地址的网段。
相信好多人都和偶一样,认为IP只要前三段相同,就是在同一网段了,其实,不是这样的,同样,我样把IP的每一段转换为一个二进制数,这里就拿IP:192.168.0.1,子网掩码:255.255.255.0做实验吧。
192.168.0.1
11000000.10101000.00000000.00000001
(这里说明一下,和子网掩码一样,每段8位,不足8位的,前面加0补齐。)
IP 11000000.10101000.00000000.00000001
子网掩码 11111111.11111111.11111111.00000000
在这里,向大家说一下到底怎么样才算同一网段。
要想在同一网段,必需做到网络标识相同,那网络标识怎么算呢?各类IP的网络标识算法都是不一样的。A类的,只算第一段。B类,只算第一、二段。C类,算第一、二、三段。
算法只要把IP和子网掩码的每位数AND就可以了。
AND方法:0和1=0 0和0=0 1和1=1
如:And 192.168.0.1,255.255.255.0,先转换为二进制,然后AND每一位
IP 11000000.10101000.00000000.00000001
子网掩码 11111111.11111111.11111111.00000000
得出AND结果 11000000.10101000.00000000.00000000
转换为十进制192.168.0.0,这就是网络标识,
再将子网掩码反取,也就是00000000.00000000.00000000.11111111,与IP AND
得出结果00000000.00000000.00000000.00000001,转换为10进制,即0.0.0.1,
这0.0.0.1就是主机标识。要想在同一网段,必需做到网络标识一样。
我们再来看看这个改变默认子网掩码的B类IP
如IP:188.188.0.111,188.188.5.222,子网掩码都设为255.255.254.0,在同一网段吗?
先将这些转换成二进制
188.188.0.111 10111100.10111100.00000000.01101111
188.188.5.222 10111100.10111100.00000101.11011010
255.255.254.0 11111111.11111111.11111110.00000000
分别AND,得
10111100.10111100.00000000.00000000
10111100.10111100.00000100.00000000
网络标识不一样,即不在同一网段。
-------------------Reference:https://www.cnblogs.com/way_testlife/archive/2010/10/05/1844399.html
The problem: https://www.nowcoder.com/practice/de538edd6f7e4bc3a5689723a7435682?tpId=37&tqId=21241&tPage=1&rp=&ru=/ta/huawei&qru=/ta/huawei/question-ranking
在更进一步的探索前打下一些基础:
关于string --->int的转换:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int main() 4 { 5 string a="123456"; 6 int c=atoi(a.c_str()); 7 cout<<c%100<<endl; 8 return 0; 9 } //显示56.
参考链接:https://www.cnblogs.com/skunk/archive/2009/05/06/1450903.html
夏夜拾锦(我喜欢那种循序渐进水到渠成之感,加油冲呀!)
255.255.255.255 为非法子网掩码(题目的意思,实际这个掩码,也能用)
Analysis adventure:
判断子网掩码是否有效
通过判断是否为255.255.255.255 如果是的话,错误加一,继续下次循环
判断别的子网掩码是否正确。根据子网掩码二进制规律(开头为连续的1,然后为0),我们将子网掩码按位取反,然后加一,得到的新二进制位,然后我们通过判断二进制中1的个数来判断是否为合法的子网掩码。(因为合法的子网掩码,按位取反,加一后,二进制位中,只有一个1)(这真的是很厉害的校验是否是正确的子网掩码的方法,对位运算的理解很深刻 学习了)
codes piece:
清风朗月正相伴,先介绍一下位运算的相关知识:
First: 按位与(And) (&)运算 将参与运算的两操作数各自对应的二进制位进行与操作。
intro: 将参与运算的两操作数各自对应的二进制位进行与操作。
例如:6的二进制是110,11的二进制是1011,那么6 & 11的结果就是2
例如,需要将int型变量n的低8位全置成0,而其余位不变,则用:
n = n & 0xFFFFFF00
& 也常用于二进制取位操作,例如一个数 & 1的结果就是取二进制的最末位。如果要判断n的第8位(从右往左,从1开始数)是否是1,则用:
if (n & 0x80 == 0x80) 语句
附注:int型是32个二进制位,16进制整数每个数字代表4个二进制位,故16进制int型常量最多是8位。(紫色是神秘之色,以寄未知浩渺)
--------------------------------------------https://blog.csdn.net/c20180630/article/details/57076374
#include<bits/stdc++.h> using namespace std; int main() { int a=3;//00000011 int b=5;//00000101 printf("%d",a&b);// it couts 1 3&5=1 }
A trick:
用于消去x的最后一位1.
x & (x-1) x = 1100 x-1 = 1011 x & (x-1) = 1000
Second: 左移(<<)运算
a << b就表示把a转为二进制后左移b位(在后面添b个0)。例如100的二进制为1100100,而110010000转成十进制是400,那么100 << 2 = 400。可以看出,a << b的值实际上就是a乘以2的b次方,因为在二进制数后添一个0就相当于该数乘以2
(这样做要求保证高位的1不被移出)。
通常认为a << 1比a * 2更快,因为前者是更底层一些的操作。因此程序中乘以2的操作请尽量用左移一位来代替。
定义常量时可以用<<运算。你可以方便地用(1 << 16) - 1来表示65535。很多算法和数据结构要求数据规模必须是2的幂,此时可以用<<来定义MAXN等常量。
Third: 右移(>>)运算
a >> b表示二进制右移b位(去掉末b位)。
当a是正整数时,a>>b等价于a/(2的b次方)
当a是负整数时,a>>b并不等价与a/(2的b次方),而是等于a/(2的b次方)上取整。
如a=-9
cout<<a/2; //输出-4.
cout<<(a>>1); //输出-5.
那个子网掩码的判断函数如下:
int validMask(char *p) { int flag,i ; unsigned int b1 = 0, n[4]; sscanf(p, "%u.%u.%u.%u", &n[3], &n[2], &n[1], &n[0]); //sscanf参见https://www.cnblogs.com/hejing-swust/p/7793958.html if(n[0] == 255 &&n[1] == 255 &&n[2] == 255 &&n[3] == 255 ) { flag = false; return flag; } for(i = 0; i < 4; ++i) b1 += n[i] << (i * 8); b1 = ~b1 + 1; //很等待我去探索丫! if((b1 & (b1 - 1)) == 0) { flag = true; } else flag = false; return flag; }
Here is my version:
1 int validMask(char *p) 2 { 3 int flag,i; 4 unsigned int b1=0,n[4]; 5 sscanf(p,"%u.%u.%u.%u",&n[3],&n[2],&n[1],&n[0]); 6 if(n[0]==255 && n[1]==255 && n[2]==255 && n[3]==255){ 7 flag=false; 8 return flag; 9 } 10 for(i=0;i<4;i++) b1+=n[i]<<(i*8); 11 b1=~b1+1; 12 if(b1 & (b1-1) ==0) flag=true;//判断二进制中1的个数来判断是否为合法的子网掩码 13 else flag=false; 14 return flag; 15 }
Then............
我的借鉴的代码是这样的形式的:
1 #include <stdio.h> 2 #include <arpa/inet.h> 3 #include <sys/socket.h> 4 #include <netinet/in.h> 5 #include <strings.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #define true 1 9 #define false 0 10 int validMask(char *p) { 11 int flag,i ; 12 unsigned int b1 = 0, n[4]; 13 sscanf(p, "%u.%u.%u.%u", &n[3], &n[2], &n[1], &n[0]); 14 if(n[0] == 255 &&n[1] == 255 &&n[2] == 255 &&n[3] == 255 ) { 15 flag = false; 16 return flag; 17 } 18 for(i = 0; i < 4; ++i) 19 b1 += n[i] << (i * 8); 20 b1 = ~b1 + 1; 21 if((b1 & (b1 - 1)) == 0) { 22 flag = true; 23 } else 24 flag = false; 25 return flag; 26 } 27 int main() { 28 char str[50]; 29 int a =0, b = 0, c = 0, d = 0, e = 0, err = 0, pri = 0; 30 while(fgets(str,50,stdin)) { 31 char *tok = str; 32 char p[2][20] = {0}; 33 int i = 0; 34 while ((tok = strtok(tok, "~")) != NULL) { 35 strcpy(p[i], tok); 36 tok = NULL; 37 i ++ ; 38 if(i == 2) 39 i = 0; 40 } 41 int flag = validMask(p[1]); 42 if(flag) { 43 struct in_addr s; 44 unsigned int ip1,ip2; 45 int valid = inet_pton(AF_INET,p[0],(void *)&s); 46 sscanf(p[0],"%u.%u",&ip1,&ip2); 47 if(valid) { 48 if(ip1>=1 && ip1 <=126) 49 a++; 50 else if(ip1>=128 && ip1 <=191) 51 b++; 52 else if(ip1>=192 && ip1 <=223) 53 c++; 54 else if(ip1>=224 && ip1 <=239) 55 d++; 56 else if(ip1>=240 && ip1 <=255) 57 e++; 58 if(ip1==10 59 || (ip1==172 && ip2 >=16 &&ip2 <=31) 60 || (ip1==192 && ip2 ==168)) 61 pri ++; 62 } else 63 err ++; 64 } else 65 err++; 66 } 67 printf("%d %d %d %d %d %d %d\n",a,b,c,d,e,err,pri); 68 return 0; 69 }
有很多的细节来处理的,加油!一会儿我再来补述哦!