识别有效的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   

 

 110                       

 

&  1011

 

------------

 

   0010  -->  2        & 运算常常用来将某变量的某些位清0,而保留其它位不变。

例如,需要将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 }

有很多的细节来处理的,加油!一会儿我再来补述哦!

posted @ 2019-07-23 20:37  龙龙666666  阅读(1749)  评论(0编辑  收藏  举报