欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中IP段轉(zhuǎn)CIDR的原理與實(shí)現(xiàn)詳解

 更新時(shí)間:2025年03月03日 09:28:19   作者:伏羲棧  
CIDR表示的是無(wú)類別域間路由,通常形式是IP地址后跟一個(gè)斜杠和數(shù)字,這篇文章主要為大家介紹了如何使用Java實(shí)現(xiàn)IP段轉(zhuǎn)CIDR,需要的可以了解下

首先,需要確認(rèn)CIDR是什么?CIDR表示的是無(wú)類別域間路由,通常形式是IP地址后跟一個(gè)斜杠和數(shù)字,比如192.168.1.0/24。這個(gè)數(shù)字表示網(wǎng)絡(luò)前綴的位數(shù),剩下的位數(shù)用于主機(jī)地址。CIDR的作用是更高效地分配IP地址,減少浪費(fèi)。

我們現(xiàn)在要實(shí)現(xiàn),將給定的起始IP和結(jié)束IP轉(zhuǎn)換為CIDR塊。那么,如何從兩個(gè)IP地址中找到覆蓋它們的CIDR呢?可能需要分解成多個(gè)CIDR塊,因?yàn)閱蝹€(gè)CIDR可能無(wú)法覆蓋整個(gè)范圍,尤其是當(dāng)范圍不是連續(xù)的2的冪次方時(shí)。

一、需要考慮以下幾點(diǎn)

將IP地址轉(zhuǎn)換為整數(shù):IPv4地址是32位的,可以將每個(gè)部分轉(zhuǎn)換為二進(jìn)制,然后組合成一個(gè)長(zhǎng)整數(shù),這樣便于計(jì)算。例如,192.168.1.0轉(zhuǎn)換為整數(shù)是3232235776。

確定起始和結(jié)束IP的整數(shù)形式:需要將輸入的字符串拆分四個(gè)部分,每個(gè)部分轉(zhuǎn)換為整數(shù),并進(jìn)行位移組合成一個(gè)長(zhǎng)整型。

計(jì)算CIDR塊:從起始地址開(kāi)始,找到最大的前綴長(zhǎng)度(即最小的子網(wǎng)),使得該子網(wǎng)包含在起始和結(jié)束范圍內(nèi)。然后,將起始地址遞增到下一個(gè)子網(wǎng)的起始地址,重復(fù)這個(gè)過(guò)程直到覆蓋整個(gè)范圍。

這里的關(guān)鍵步驟是如何找到每個(gè)可能的CIDR塊。例如,假設(shè)起始IP是192.168.0.0,結(jié)束IP是192.168.3.255。這個(gè)范圍可以被分解為192.168.0.0/23或者192.168.0.0/24和192.168.1.0/24以及192.168.2.0/23等,但需要找到最合適的分解方式。

二、可能的算法步驟

  • 將起始IP和結(jié)束IP轉(zhuǎn)換為長(zhǎng)整數(shù)。
  • 如果起始IP大于結(jié)束IP,則交換它們。
  • 初始化一個(gè)列表來(lái)保存CIDR塊。
  • 循環(huán)處理,直到起始IP超過(guò)結(jié)束IP:
  • 計(jì)算起始IP的最低位有效位(即最大的后綴0的位數(shù)),這決定了當(dāng)前可能的最大CIDR塊。
  • 同時(shí),確定當(dāng)前塊的大小,不超過(guò)結(jié)束IP。
  • 確定實(shí)際可以使用的掩碼位數(shù),使得該子網(wǎng)不超過(guò)結(jié)束IP。
  • 將CIDR塊添加到列表。
  • 將起始IP加上當(dāng)前塊的大小,繼續(xù)處理。

這里需要注意的是,每次找到的CIDR塊應(yīng)該是最大的可能塊,以減少塊的數(shù)量。例如,如果起始IP是192.168.0.0,結(jié)束IP是192.168.0.255,那么CIDR是/24。但如果結(jié)束IP是192.168.1.127,可能需要分解為多個(gè)塊,如/24和/25等。

在Java中實(shí)現(xiàn)時(shí),需要處理大整數(shù),因?yàn)镮Pv4是32位,可以使用long類型來(lái)避免符號(hào)問(wèn)題。同時(shí),掩碼的計(jì)算可能需要位運(yùn)算,比如找出最長(zhǎng)的公共前綴。

另外,還需要處理一些特殊情況,比如起始和結(jié)束IP相同,這時(shí)候CIDR就是/32?;蛘咂鹗糏P到結(jié)束IP是一個(gè)連續(xù)的塊,但無(wú)法被一個(gè)CIDR覆蓋,需要拆分成多個(gè)。

例如,起始IP是192.168.0.5,結(jié)束IP是192.168.0.7。這時(shí)候CIDR列表應(yīng)該是192.168.0.5/32, 192.168.0.6/31,或者類似的組合。但需要確保每個(gè)CIDR塊覆蓋的IP范圍在起始和結(jié)束之間。

三、Java程序?qū)崿F(xiàn)步驟

現(xiàn)在,需要將這些邏輯轉(zhuǎn)化為Java代碼,需要以下步驟:

  • 編寫一個(gè)方法將IP字符串轉(zhuǎn)換為長(zhǎng)整數(shù)。
  • 編寫一個(gè)方法將長(zhǎng)整數(shù)轉(zhuǎn)換回IP字符串。
  • 編寫方法計(jì)算兩個(gè)IP之間的CIDR塊列表。

在轉(zhuǎn)換IP到長(zhǎng)整數(shù)時(shí),需要將每個(gè)部分左移相應(yīng)的位數(shù),例如第一個(gè)部分左移24位,第二個(gè)左移16位,依此類推。同時(shí),確保每個(gè)部分的數(shù)值在0-255之間。

在計(jì)算CIDR塊時(shí),需要找到最大的掩碼位數(shù),使得該子網(wǎng)的起始地址加上塊大小不超過(guò)結(jié)束IP。塊大小是2^(32 - mask),所以需要找到最大的mask,使得start + size <= end + 1?;蛘撸硪环N方式是找到起始IP的最低位有效位,然后逐步調(diào)整掩碼位數(shù)。

3.1 如何計(jì)算mask?

可以計(jì)算從start到end的差值為diff = end - start +1。然后,最大的塊是最大的2的冪次方,不大于diff,并且塊的大小還要考慮start的低位是否為0。

或者,找到從start開(kāi)始的最低位有效位,例如,計(jì)算start的二進(jìn)制中最低位的1的位置,然后塊的大小是2^該位置,但是不能超過(guò)end - start +1。

例如,start的二進(jìn)制是x,那么最大的塊大小是最大的2的冪次方,使得塊大小 <= diff,并且塊大小是start的二進(jìn)制中最低位1所代表的值。

例如,假設(shè)start是192.168.0.5(轉(zhuǎn)換為整數(shù)是3232235525),二進(jìn)制是…00000101。最低位的1的位置是0(因?yàn)樽詈笠晃皇?),所以塊大小是1,即mask是32。這樣該塊只包含start自己。但如果diff大于1,那么可能之后的下一個(gè)塊可以更大。

或者,這可能不是最優(yōu)的方式。正確的做法應(yīng)該是找到最大的mask,使得該子網(wǎng)覆蓋的起始地址是start,并且子網(wǎng)的結(jié)束地址不超過(guò)end。

這可能需要計(jì)算該子網(wǎng)的網(wǎng)絡(luò)地址,然后找到最大的mask,使得該子網(wǎng)的廣播地址不超過(guò)end。

例如,子網(wǎng)的網(wǎng)絡(luò)地址是start & mask,而廣播地址是network | (~mask & 0xFFFFFFFFL)。所以需要確保廣播地址 <= end。

但這個(gè)過(guò)程可能比較復(fù)雜。因此,可能需要另一種方法:

對(duì)于當(dāng)前的start,嘗試找到最大的mask,使得該mask對(duì)應(yīng)的塊大?。?^(32 - mask))使得start + block_size -1 <= end.

同時(shí),mask必須滿足start的二進(jìn)制中,后(32 - mask)位必須全為0,否則該子網(wǎng)的起始地址不是start,而是更小的地址。

例如,如果start是192.168.0.5,而mask是24,那么子網(wǎng)的起始地址是192.168.0.0,而start不在該子網(wǎng)的起始地址,因此無(wú)法使用mask為24。因此,必須找到最大的mask,使得start是該子網(wǎng)的起始地址,即start的后(32 - mask)位必須是0。

因此,正確的步驟應(yīng)該是:

計(jì)算從start開(kāi)始,最大的后綴0的位數(shù)。這可以通過(guò)計(jì)算start的最低位1的位置,比如start的二進(jìn)制中有n個(gè)后綴0,那么最大的mask是32 - n。此時(shí),塊大小是2^n。例如,start是192.168.0.0(即…00000000),那么n是8,mask是24,塊大小是256。

但如果start是192.168.0.128,二進(jìn)制是…10000000,那么后綴0的位數(shù)是7,所以mask是25,塊大小是128。此時(shí),子網(wǎng)是192.168.0.128/25,廣播地址是192.168.0.255。

但是,如果end是192.168.0.255,那么這個(gè)塊可以包含。但是如果end是更小的值,比如192.168.0.200,那么需要調(diào)整mask,使得塊大小不超過(guò)end - start +1。

因此,mask的確定應(yīng)該是:

mask = 32 - Math.min(lowBit, log2(remainingSize))

其中,lowBit是start的最低位1的位置,remainingSize是end - start +1。

或者,另一種方法是:

mask = 32 - Math.min(lowBit, 32 - (int)(Math.log(remainingSize)/Math.log(2)))

但是,計(jì)算lowBit的方式需要正確。例如,對(duì)于start的二進(jìn)制,找到最右邊1的位置。比如,start的二進(jìn)制是…0001000,那么lowBit是3(從0開(kāi)始計(jì)數(shù)),即2^3=8。所以塊的大小是8,mask是32 -3 =29。

3.2 如何計(jì)算lowBit呢?

可以用位運(yùn)算來(lái)找最右邊的1的位置。例如,對(duì)于long類型的start,取值為start & -start,這會(huì)得到一個(gè)只有最右邊1的數(shù),然后計(jì)算log2。

例如,start & -start可以得到一個(gè)數(shù),其中只有最右邊的1被保留。例如,start是5(二進(jìn)制101),start & -start的結(jié)果是1(二進(jìn)制1),所以log2(1)=0,即lowBit=0。此時(shí),塊的大小是1,mask=32-0=32.

如果是start是8(二進(jìn)制1000),則start & -start=8,log2(8)=3,所以lowBit=3,塊的大小是8,mask=29.

所以,lowBit = log2(start & -start). 但如何計(jì)算log2?可以用位運(yùn)算,或者循環(huán)右移。

在Java中,可以使用Long.numberOfTrailingZeros()方法,這個(gè)方法返回的是二進(jìn)制中最低位1后面的0的個(gè)數(shù)。例如,對(duì)于8(1000),numberOfTrailingZeros是3。此時(shí),lowBit就是這個(gè)值,即塊的大小是1 << lowBit。

但這個(gè)方法是否適用于所有情況?

假設(shè)start是0,那么start & -start是0,這可能有問(wèn)題。但start在這里是介于起始和結(jié)束之間的,所以可能不會(huì)為0?

但起始IP可能為0,例如0.0.0.0到0.0.0.255。此時(shí),lowBit是8,塊的大小是256,mask是24.

所以,lowBit的計(jì)算應(yīng)該是:

long diff = start & -start;

如果diff ==0,那么lowBit=32?或者如何處理?

或者,另一種方式,使用Long.numberOfTrailingZeros(startIpLong)來(lái)獲取最低位1后面的0的個(gè)數(shù)。例如,對(duì)于startIpLong=8(二進(jìn)制1000),這個(gè)方法會(huì)返回3,因?yàn)槟┪灿?個(gè)0。此時(shí),塊的大小是1 << 3 =8.

所以,lowBit = Long.numberOfTrailingZeros(startIpLong | 0xFFFFFFFF00000000L)? 不太確定,可能需要仔細(xì)測(cè)試。

或者,對(duì)于IPv4的32位,startIpLong是32位的數(shù)值,存儲(chǔ)在long類型的高32位或者低32位?可能要看轉(zhuǎn)換方式是否正確。例如,前面的轉(zhuǎn)換函數(shù)是否正確地將IP轉(zhuǎn)換為長(zhǎng)整數(shù),比如將第一個(gè)八位移到高位。

例如,IP地址a.b.c.d轉(zhuǎn)換為長(zhǎng)整數(shù)的方式是:a<<24 | b<<16 | c<<8 | d。這樣,IPv4的32位被放在long的高32位還是低32位?

假設(shè)轉(zhuǎn)換正確的話,那么對(duì)于192.168.0.5,轉(zhuǎn)換為長(zhǎng)整數(shù)是 (192 <<24) | (168<<16) | (0<<8) |5 = 3232235525。二進(jìn)制表示為:

11000000 10101000 00000000 00000101

這里,最低位的1是在第0位(最后一個(gè)字節(jié)的5是00000101,最低位是1)。所以,Long.numberOfTrailingZeros(3232235525)應(yīng)該是0,因?yàn)樽畹臀皇?,后面沒(méi)有0。所以塊的大小是1,mask是32-0=32。

這樣,每個(gè)塊的大小是根據(jù)start的最低位1的位置決定的,但也要確保塊的大小不超過(guò)剩余的地址空間(即end - start +1)。

3.3 綜合起來(lái),步驟如下

將startIp和endIp轉(zhuǎn)換為長(zhǎng)整數(shù)s和e,確保s <=e。

初始化結(jié)果列表。

當(dāng)s <= e時(shí),循環(huán):

  • a. 計(jì)算當(dāng)前s的最低位1的位置,得到lowBit = Long.numberOfTrailingZeros(s).
  • b. 可能的塊大小maxSize = 1 << lowBit.
  • c. 但maxSize不能超過(guò)e - s +1,所以實(shí)際塊大小是Math.min(maxSize, e -s +1).
  • d. 這時(shí)候,需要找到最大的mask,使得塊大小為actualSize。因?yàn)閍ctualSize必須是2的冪,所以mask = 32 - log2(actualSize).
  • e. 所以,mask = 32 - (int)(Math.log(actualSize)/Math.log(2)).
  • f. 將CIDR塊添加到結(jié)果列表:IP/mask.
  • g. s += actualSize.

這樣,每次循環(huán)處理一個(gè)塊,直到覆蓋整個(gè)范圍。

例如,start=192.168.0.5,end=192.168.0.7:

s=3232235525 (0xC0A80005), e=3232235527.

第一次循環(huán):

  • lowBit = trailing zeros of s=5(二進(jìn)制101),即0位,所以maxSize=1<<0=1.
  • actualSize=min(1, 7-5+1=3) →1.
  • mask=32 -0=32.
  • CIDR塊是192.168.0.5/32.
  • s +=1 →6.

第二次循環(huán):

  • s=6,二進(jìn)制00000110,trailing zeros是1(因?yàn)樽詈髢晌皇?10 →最低位是0?)
  • 不,Long.numberOfTrailingZeros(6)是1,因?yàn)?的二進(jìn)制是110,末尾有一個(gè)0。
  • 所以,lowBit=1 →maxSize=2.
  • actualSize=min(2, 7-6+1=2) →2.
  • mask=32 -1=31.
  • CIDR塊是192.168.0.6/31,覆蓋6和7.
  • s +=2 →8,超出e=7,循環(huán)結(jié)束。

所以,結(jié)果是兩個(gè)CIDR塊:/32和/31.

這似乎正確。

另一個(gè)例子,start=192.168.0.0,end=192.168.3.255:

s=3232235520, e=3232238079.

第一次循環(huán):

lowBit=Long.numberOfTrailingZeros(s) →s=0xC0A80000 →二進(jìn)制是11000000 10101000 00000000 00000000 →末尾有16個(gè)零?或者可能要看具體的轉(zhuǎn)換方式。

如果IP地址轉(zhuǎn)換為長(zhǎng)整數(shù)的方式是每個(gè)八位作為高位,例如:

對(duì)于192.168.0.0,轉(zhuǎn)換為:

192 <<24 | 168 <<16 | 0 <<8 |0 →即3232235520,二進(jìn)制是11000000 10101000 00000000 00000000。

Long.numberOfTrailingZeros(3232235520)→計(jì)算末尾的0的個(gè)數(shù)。3232235520的二進(jìn)制是:

11000000 10101000 00000000 00000000 →最后的16位都是0,所以 trailing zeros是16.

所以,lowBit=16 →maxSize=1<<16=65536.

但實(shí)際剩余的大小是 e -s +1 = 3232238079 -3232235520 +1=2560.

所以actualSize=min(65536, 2560)=2560.

但2560不是2的冪次方。這時(shí)候需要調(diào)整,找到最大的2的冪次方,不超過(guò)2560.

所以,這里可能我的之前的思路有問(wèn)題,即塊的大小必須是2的冪次方,而實(shí)際剩余的大小可能不是,因此需要取最大可能的塊,其大小是2的冪次方,并且不超過(guò)剩余的大小。

這時(shí)候,應(yīng)該怎么做?

例如,在步驟c中,maxSize由lowBit決定,即可能的塊大小是2^lowBit,但可能這個(gè)大小超過(guò)了剩余的大小,因此需要將actualSize設(shè)置為最大的2的冪次方,不超過(guò)剩余的大小。

或者,另一種方式,actualSize是最大的2的冪次方,使得actualSize <= remainingSize,并且actualSize <= maxSize(即由lowBit決定的塊大小)。

例如,remainingSize是2560,那么最大的2的冪次方是2048(2^11=2048),而maxSize由lowBit=16是65536,所以actualSize=2048.

此時(shí),mask=32 -11=21.

這樣,CIDR塊是192.168.0.0/21?但是這樣的話,該塊的范圍是192.168.0.0到192.168.7.255,但用戶的結(jié)束IP是192.168.3.255,因此這樣會(huì)超出范圍。這說(shuō)明我的邏輯有問(wèn)題。

哦,這說(shuō)明原來(lái)的算法可能存在問(wèn)題??赡苄枰匦驴紤]如何確定塊的大小。

正確的邏輯應(yīng)該是,找到最大的mask,使得該子網(wǎng)的起始地址是s,并且該子網(wǎng)的結(jié)束地址 <=e.

也就是說(shuō),子網(wǎng)的掩碼決定了塊的大小是2^(32 - mask),所以我們需要找到最大的mask(即最小的塊大?。?,使得 s + (2^(32 - mask)) -1 <=e.

同時(shí),該mask必須滿足s的后(32 - mask)位是0,也就是s是該子網(wǎng)的起始地址。

例如,對(duì)于s=192.168.0.0,e=192.168.3.255.

最大的mask是22,因?yàn)閴K大小是2(32-22)=210=1024,而s+1024-1=192.168.3.255。這正是需要的。

所以,正確的CIDR是192.168.0.0/22.

因此,在這種情況下,算法應(yīng)該返回一個(gè)CIDR塊,而不是多個(gè)。那問(wèn)題出在哪里?

塊的大小應(yīng)該同時(shí)滿足:

該塊的大小是2的冪次方,并且該塊的起始地址是s。

該塊的結(jié)束地址不超過(guò)e.

所以,塊的大小應(yīng)該是最大的可能的2的冪次方,滿足 s + size -1 <=e,并且 size是最大的可能值,并且s mod size ==0.

3.4 如何計(jì)算這樣的size?

算法步驟:

計(jì)算remaining = e - s +1.

初始size為最大的可能,即最大的2的冪次方 <= remaining.

同時(shí),size必須滿足 s & (size -1) ==0 (即s是該size的倍數(shù))。

如果這兩個(gè)條件滿足,則size可以是該值,否則,需要將size減半,直到滿足條件。

例如,對(duì)于s=192.168.0.0,e=192.168.3.255,remaining=1024.

最大的size是1024,且s mod 1024 ==0,所以可以使用size=1024,mask=22.

所以,正確的CIDR是/22.

那如何將此邏輯轉(zhuǎn)化為代碼?

實(shí)現(xiàn)步驟:

計(jì)算remaining = e -s +1.

計(jì)算maxMask = 32 - (int)(Math.log(remaining)/Math.log(2))→但這可能不正確,因?yàn)閞emaining可能不是2的冪次方。

或者,找到最大的mask,使得 2^(32 - mask) <= remaining,并且 s & (2^(32 -mask) -1) ==0.

正確的算法應(yīng)該是:

循環(huán)處理,每次找到最大的size(2的冪次方),使得:

size <= remaining

并且 s + size -1 <=e

并且 s & (size -1) ==0 (即s是size的整數(shù)倍)

然后,mask=32 - log2(size).

然后,將CIDR塊加入列表,s += size,remaining = e -s +1.

重復(fù)直到remaining=0.

現(xiàn)在,如何在代碼中實(shí)現(xiàn)這一點(diǎn)?

步驟:

while s <=e:
remaining = e -s +1;
maxSize = 1 << 30; // 初始為較大的數(shù),比如2^30
size = 0;
for (int shift = 0; shift <=32; shift++) {
	currentSize = 1 << shift;
	if (currentSize > remaining) break;
	if ((s & (currentSize -1)) ==0) {
		size = currentSize;
	}
}
// 此時(shí)size是最大的滿足條件的size
mask = 32 - log2(size) →即mask=32 - shift.
add cidr: ipToString(s) + "/" + mask
s += size

但這樣可能需要一個(gè)循環(huán)來(lái)找到最大的size。

或者,可以使用位運(yùn)算來(lái)找到最大的可能size:

size = remaining;

然后,找到最大的size,是2的冪次方,且滿足 size <= remaining,并且 (s & (size -1)) ==0.

例如:

size = Integer.highestOneBit(remaining); // 最大的2的冪次方 <= remaining
while (size >0) {
    if ((s & (size -1)) ==0) {
        break;
    }
    size >>=1;
}

這樣,size被調(diào)整為最大的滿足條件的size.

例如,計(jì)算size的方法:

long size = Long.highestOneBit(remaining);

然后,循環(huán)檢查 (s & (size -1)) ==0,否則將size減半,直到找到滿足條件的size.

代碼可能如下:

long remaining = e - s +1;
long size = Long.highestOneBit(remaining);
while (size >0) {
	if ((s & (size -1)) ==0) {
		break;
	}
	size >>=1;
}

if (size ==0) {
	size =1;
}

然后,mask=32 - log2(size). 但如何計(jì)算log2(size)?

可以用Long.numberOfLeadingZeros來(lái)得到。例如,對(duì)于size=1024,二進(jìn)制是10000000000,共11位,所以numberOfLeadingZeros是64-11=53。但Long.numberOfLeadingZeros(1024)返回的是64 - 11 =53?這可能需要調(diào)整。

或者,計(jì)算log2(size)可以使用位運(yùn)算:

int logSize = 63 - Long.numberOfLeadingZeros(size);

例如,size=1024 →63 - 53(numberOfLeadingZeros(1024))是10 →log2(1024)=10.

所以,mask=32 - logSize.

這樣,例如size=1024 →mask=22.

所以,綜合以上分析,Java代碼的大致結(jié)構(gòu)可以是:

public static List<String> ipRangeToCidrs(String startIp, String endIp) {

	// 轉(zhuǎn)換startIp和endIp為long s和e
	List<String> cidrs = new ArrayList<>();
	long s = ipToLong(startIp);
	long e = ipToLong(endIp);
	while (s <= e) {
		// 計(jì)算remaining
		long remaining = e - s +1;
		
		// 計(jì)算最大的size
		long size = 1;
		int shift =0;
		
		// 找到最大的size=2^shift <= remaining,并且s & (size-1) ==0
		for (shift =0; (1L << (shift+1)) <= remaining; shift++) {
			if ((s & ( (1L << (shift+1)) -1 )) !=0) {
				break;
			}
		}
		
		size = 1L << shift;
		
		// 計(jì)算mask
		int mask = 32 - shift;
		
		// 添加CIDR
		cidrs.add(longToIp(s) + "/" + mask);
		
		// 更新s
		s += size;
	}
	
	return cidrs;
}

四、ipToLong函數(shù)

public static long ipToLong(String ip) {

	String[] octets = ip.split("\\.");
	
	if (octets.length !=4) {
		throw new IllegalArgumentException("Invalid IP address");
	}
	
	long result =0;
	for (int i=0; i<4; i++) {
		int octet = Integer.parseInt(octets[i]);
		if (octet <0 || octet >255) {
			throw new IllegalArgumentException("Invalid IP octet");
		}
		result <<=8;
		result |= octet;
	}
	
	return result;
}

這樣,對(duì)于192.168.0.5,結(jié)果是 (192 <<24) | (168 <<16) | (0 <<8) |5 = 3232235525L.

五、longToIp函數(shù)

public static String longToIp(long ip) {
	return String.format("%d.%d.%d.%d",
	(ip >>24) & 0xFF,
	(ip >>16) & 0xFF,
	(ip >>8) & 0xFF,
	ip & 0xFF);
}

這樣,3232235525L會(huì)被正確轉(zhuǎn)換為192.168.0.5.

六、編寫主要的循環(huán)部分

public static List<String> ipRangeToCidr(String startIp, String endIp) {
	long start = ipToLong(startIp);
	long end = ipToLong(endIp);
	List<String> result = new ArrayList<>();
	if (start > end) {
		throw new IllegalArgumentException("Start IP must be <= End IP");
	}
	while (start <= end) {
		// 計(jì)算當(dāng)前start的最低位1的位置,確定最大的可能的mask
		long maxSize = 1;
		int shift =0;
		
		// 找到最大的shift,使得 (1 << shift) <= remaining,并且 (start & ((1 << shift) -1)) ==0
		long remaining = end - start +1;
		
		// 初始shift為0,逐步增加,直到無(wú)法滿足條件
		for (shift =0; (1L << (shift+1)) <= remaining; shift++) {
		
			// 檢查如果增加shift后,是否start的后面的位都是0
			long mask = (1L << (shift +1)) -1;
			if ((start & mask) !=0) {
				break;
			}
		}
		long currentSize = 1L << shift;
		int mask = 32 - shift;
		result.add(longToIp(start) + "/" + mask);
		start += currentSize;
	}
	return result;
}

綜上所述,最終的Java函數(shù)應(yīng)該包括:

ipToLong和longToIp的轉(zhuǎn)換函數(shù)。

主函數(shù)處理循環(huán),找到每個(gè)可能的CIDR塊。

正確計(jì)算size和mask.

七、完整代碼實(shí)現(xiàn)

以下是完整的Java 代碼,并處理可能的異常和邊界情況,可以將起始 IP 和結(jié)束 IP 轉(zhuǎn)換為最精確的 CIDR 塊列表。該方法通過(guò)位運(yùn)算動(dòng)態(tài)計(jì)算每個(gè) CIDR 塊的范圍,確保覆蓋所有 IP 且不重疊

import java.util.ArrayList;
import java.util.List;

public class CidrCalculator {

    public static List<String> convertIpRangeToCidr(String startIp, String endIp) {
        long start = ipToLong(startIp);
        long end = ipToLong(endIp);
        List<String> cidrList = new ArrayList<>();

        if (start > end) {
            throw new IllegalArgumentException("起始IP不能大于結(jié)束IP");
        }

        while (start <= end) {
            // 計(jì)算當(dāng)前起始IP的最低位有效位(決定CIDR塊的大?。?
            int suffixZeros = Long.numberOfTrailingZeros(start);
            long maxSize = 1L << suffixZeros;  // 可能的塊大小(2^suffixZeros)
            long remaining = end - start + 1;  // 剩余需要覆蓋的IP數(shù)量

            // 實(shí)際塊大小不能超過(guò)剩余范圍
            long actualSize = Math.min(maxSize, remaining);
            
            // 計(jì)算CIDR的掩碼位數(shù)(32 - log2(actualSize))
            int mask = 32 - (int) (Math.log(actualSize) / Math.log(2));
            
            // 將CIDR塊加入列表
            cidrList.add(longToIp(start) + "/" + mask);
            
            // 移動(dòng)到下一個(gè)塊的起始IP
            start += actualSize;
        }

        return cidrList;
    }

    // 將IP字符串轉(zhuǎn)換為32位整數(shù)(用long存儲(chǔ))
    private static long ipToLong(String ip) {
        String[] octets = ip.split("\\.");
        if (octets.length != 4) {
            throw new IllegalArgumentException("無(wú)效的IP地址格式");
        }
        long result = 0;
        for (int i = 0; i < 4; i++) {
            int octet = Integer.parseInt(octets[i]);
            if (octet < 0 || octet > 255) {
                throw new IllegalArgumentException("IP八位組超出范圍");
            }
            result = (result << 8) | octet;
        }
        return result;
    }

    // 將32位整數(shù)轉(zhuǎn)換為IP字符串
    private static String longToIp(long ip) {
        return ((ip >> 24) & 0xFF) + "." +
               ((ip >> 16) & 0xFF) + "." +
               ((ip >> 8) & 0xFF) + "." +
               (ip & 0xFF);
    }

    // 測(cè)試用例
    public static void main(String[] args) {
        // 示例1:?jiǎn)蝹€(gè)IP(/32)
        test("192.168.0.5", "192.168.0.5");  // ["192.168.0.5/32"]

        // 示例2:連續(xù)兩個(gè)IP(/31)
        test("192.168.0.6", "192.168.0.7");  // ["192.168.0.6/31"]

        // 示例3:一個(gè)完整的C類子網(wǎng)(/24)
        test("192.168.0.0", "192.168.0.255");  // ["192.168.0.0/24"]

        // 示例4:跨子網(wǎng)的IP范圍
        test("192.168.0.5", "192.168.0.7");  // ["192.168.0.5/32", "192.168.0.6/31"]
    }

    private static void test(String startIp, String endIp) {
        List<String> cidrs = convertIpRangeToCidr(startIp, endIp);
        System.out.println("IP范圍: " + startIp + " - " + endIp);
        System.out.println("CIDR塊: " + String.join(", ", cidrs) + "\n");
    }
}

代碼說(shuō)明

核心邏輯

  • 位運(yùn)算計(jì)算:通過(guò) Long.numberOfTrailingZeros 找到起始 IP 的最低位有效位,確定最大可能的 CIDR 塊大小。
  • 動(dòng)態(tài)調(diào)整塊大?。焊鶕?jù)剩余 IP 范圍 (remaining) 和起始 IP 的對(duì)齊要求,動(dòng)態(tài)調(diào)整實(shí)際 CIDR 塊大小。

關(guān)鍵方法

  • ipToLong():將 IP 字符串轉(zhuǎn)換為 32 位整數(shù)(用 long 存儲(chǔ),避免符號(hào)問(wèn)題)。
  • longToIp():將整數(shù)轉(zhuǎn)換回點(diǎn)分十進(jìn)制格式。
  • convertIpRangeToCidr():主方法,遍歷 IP 范圍生成 CIDR 列表。

測(cè)試案例

覆蓋單 IP、連續(xù) IP、完整子網(wǎng)和跨子網(wǎng)范圍,驗(yàn)證不同場(chǎng)景的準(zhǔn)確性。

執(zhí)行示例

輸入:

convertIpRangeToCidr("192.168.0.6", "192.168.0.7");

輸出:

CIDR塊: 192.168.0.6/31

輸入:

convertIpRangeToCidr("192.168.0.5", "192.168.0.7");

輸出:

CIDR塊: 192.168.0.5/32, 192.168.0.6/31

到此這篇關(guān)于Java中IP段轉(zhuǎn)CIDR的原理與實(shí)現(xiàn)詳解的文章就介紹到這了,更多相關(guān)Java IP段轉(zhuǎn)CIDR內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺談springMVC接收前端json數(shù)據(jù)的總結(jié)

    淺談springMVC接收前端json數(shù)據(jù)的總結(jié)

    下面小編就為大家分享一篇淺談springMVC接收前端json數(shù)據(jù)的總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • SpringBoot框架整合SwaggerUI的示例代碼

    SpringBoot框架整合SwaggerUI的示例代碼

    項(xiàng)目中使用了很多現(xiàn)成的框架,都是項(xiàng)目經(jīng)理、架構(gòu)師帶來(lái)的,從來(lái)沒(méi)有自己整合過(guò),今天給大家介紹下SpringBoot框架整合SwaggerUI的過(guò)程,感興趣的朋友跟隨小編一起看看吧
    2022-02-02
  • Java世界時(shí)區(qū)自動(dòng)計(jì)算及時(shí)間生成方法詳解

    Java世界時(shí)區(qū)自動(dòng)計(jì)算及時(shí)間生成方法詳解

    這篇文章主要為大家詳細(xì)介紹了Java中世界時(shí)區(qū)自動(dòng)計(jì)算及時(shí)間生成的方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-12-12
  • mybatis查詢字段為null設(shè)置為0的操作

    mybatis查詢字段為null設(shè)置為0的操作

    這篇文章主要介紹了mybatis查詢字段為null設(shè)置為0的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02
  • Spring Boot中如何使用Swagger詳解

    Spring Boot中如何使用Swagger詳解

    Swagger是一個(gè)規(guī)范和完整的框架,用于生成、描述、調(diào)用和可視化 RESTful風(fēng)格的Web服務(wù),這篇文章主要給大家介紹了關(guān)于Spring Boot中如何使用Swagger的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • SpringBoot集成RocketMQ發(fā)送事務(wù)消息的原理解析

    SpringBoot集成RocketMQ發(fā)送事務(wù)消息的原理解析

    RocketMQ 的事務(wù)消息提供類似 X/Open XA 的分布事務(wù)功能,通過(guò)事務(wù)消息能達(dá)到分布式事務(wù)的最終一致,這篇文章主要介紹了SpringBoot集成RocketMQ發(fā)送事務(wù)消息,需要的朋友可以參考下
    2022-06-06
  • java實(shí)現(xiàn)計(jì)算器功能

    java實(shí)現(xiàn)計(jì)算器功能

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)計(jì)算器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • 解決Idea報(bào)錯(cuò)There is not enough memory to perform the requested operation問(wèn)題

    解決Idea報(bào)錯(cuò)There is not enough memory 

    在使用Idea開(kāi)發(fā)過(guò)程中,可能會(huì)遇到因內(nèi)存不足導(dǎo)致的閃退問(wèn)題,出現(xiàn)"There is not enough memory to perform the requested operation"錯(cuò)誤時(shí),可以通過(guò)調(diào)整Idea的虛擬機(jī)選項(xiàng)來(lái)解決,方法是在Idea的Help菜單中選擇Edit Custom VM Options
    2024-11-11
  • 關(guān)于Java整合RabbitMQ實(shí)現(xiàn)生產(chǎn)消費(fèi)的7種通訊方式

    關(guān)于Java整合RabbitMQ實(shí)現(xiàn)生產(chǎn)消費(fèi)的7種通訊方式

    這篇文章主要介紹了關(guān)于Java整合RabbitMQ實(shí)現(xiàn)生產(chǎn)消費(fèi)的7種通訊方式,消息中間件是基于隊(duì)列與消息傳遞技術(shù),在網(wǎng)絡(luò)環(huán)境中為應(yīng)用系統(tǒng)提供同步或異步、可靠的消息傳輸?shù)闹涡攒浖到y(tǒng),需要的朋友可以參考下
    2023-05-05
  • Java架構(gòu)師的5大基本能力你知道嗎

    Java架構(gòu)師的5大基本能力你知道嗎

    這篇文章主要為大家介紹了Java架構(gòu)師的基本能力,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助<BR>
    2022-01-01

最新評(píng)論