IP段转CIDR:原理Java实现
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,
15年
工作经验,精通Java编程
,高并发设计
,Springboot和微服务
,熟悉Linux
,ESXI虚拟化
以及云原生Docker和K8s
,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。
技术合作请加本人wx(注明来自csdn):foreast_sea
IP段转CIDR:Java实现
首先,需要确认CIDR是什么?CIDR表示的是无类别域间路由,通常形式是IP地址后跟一个斜杠和数字,比如192.168.1.0/24。这个数字表示网络前缀的位数,剩下的位数用于主机地址。CIDR的作用是更高效地分配IP地址,减少浪费。
我们现在要实现,将给定的起始IP和结束IP转换为CIDR块。那么,如何从两个IP地址中找到覆盖它们的CIDR呢?可能需要分解成多个CIDR块,因为单个CIDR可能无法覆盖整个范围,尤其是当范围不是连续的2的幂次方时。
一、需要考虑以下几点
-
将IP地址转换为整数:IPv4地址是32位的,可以将每个部分转换为二进制,然后组合成一个长整数,这样便于计算。例如,192.168.1.0转换为整数是3232235776。
-
确定起始和结束IP的整数形式:需要将输入的字符串拆分四个部分,每个部分转换为整数,并进行位移组合成一个长整型。
-
计算CIDR块:从起始地址开始,找到最大的前缀长度(即最小的子网),使得该子网包含在起始和结束范围内。然后,将起始地址递增到下一个子网的起始地址,重复这个过程直到覆盖整个范围。
这里的关键步骤是如何找到每个可能的CIDR块。例如,假设起始IP是192.168.0.0
,结束IP是192.168.3.255
。这个范围可以被分解为192.168.0.0/23
或者192.168.0.0/24
和192.168.1.0/24
以及192.168.2.0/23
等,但需要找到最合适的分解方式。
二、可能的算法步骤
- 将起始IP和结束IP转换为长整数。
- 如果起始IP大于结束IP,则交换它们。
- 初始化一个列表来保存CIDR块。
- 循环处理,直到起始IP超过结束IP:
- 计算起始IP的最低位有效位(即最大的后缀0的位数),这决定了当前可能的最大CIDR块。
- 同时,确定当前块的大小,不超过结束IP。
- 确定实际可以使用的掩码位数,使得该子网不超过结束IP。
- 将CIDR块添加到列表。
- 将起始IP加上当前块的大小,继续处理。
这里需要注意的是,每次找到的CIDR块应该是最大的可能块,以减少块的数量。例如,如果起始IP是192.168.0.0
,结束IP是192.168.0.255
,那么CIDR
是/24
。但如果结束IP是192.168.1.127
,可能需要分解为多个块,如/24和/25等。
在Java中实现时,需要处理大整数,因为IPv4是32位,可以使用long类型来避免符号问题。同时,掩码的计算可能需要位运算,比如找出最长的公共前缀。
另外,还需要处理一些特殊情况,比如起始和结束IP相同,这时候CIDR
就是/32
。或者起始IP到结束IP是一个连续的块,但无法被一个CIDR
覆盖,需要拆分成多个。
例如,起始IP是192.168.0.5
,结束IP是192.168.0.7
。这时候CIDR列表应该是192.168.0.5/32
, 192.168.0.6/31
,或者类似的组合。但需要确保每个CIDR块覆盖的IP范围在起始和结束之间。
三、Java程序实现步骤
现在,需要将这些逻辑转化为Java代码,需要以下步骤:
-
编写一个方法将IP字符串转换为长整数。
-
编写一个方法将长整数转换回IP字符串。
-
编写方法计算两个IP之间的CIDR块列表。
在转换IP到长整数时,需要将每个部分左移相应的位数,例如第一个部分左移24位,第二个左移16位,依此类推。同时,确保每个部分的数值在0-255
之间。
在计算CIDR块时,需要找到最大的掩码位数,使得该子网的起始地址加上块大小不超过结束IP。块大小是2^(32 - mask)
,所以需要找到最大的mask,使得start + size <= end + 1
。或者,另一种方式是找到起始IP的最低位有效位,然后逐步调整掩码位数。
3.1 如何计算mask?
可以计算从start到end的差值为diff = end - start +1
。然后,最大的块是最大的2的幂次方,不大于diff,并且块的大小还要考虑start的低位是否为0。
或者,找到从start开始的最低位有效位,例如,计算start的二进制中最低位的1的位置,然后块的大小是2^该位置,但是不能超过end - start +1。
例如,start的二进制是x,那么最大的块大小是最大的2的幂次方,使得块大小 <= diff
,并且块大小是start的二进制中最低位1所代表的值。
例如,假设start是192.168.0.5
(转换为整数是3232235525),二进制是…00000101。最低位的1的位置是0(因为最后一位是1),所以块大小是1,即mask是32。这样该块只包含start自己。但如果diff大于1,那么可能之后的下一个块可以更大。
或者,这可能不是最优的方式。正确的做法应该是找到最大的mask,使得该子网覆盖的起始地址是start,并且子网的结束地址不超过end。
这可能需要计算该子网的网络地址,然后找到最大的mask,使得该子网的广播地址不超过end。
例如,子网的网络地址是start & mask,而广播地址是network | (~mask & 0xFFFFFFFFL)。所以需要确保广播地址 <= end。
但这个过程可能比较复杂。因此,可能需要另一种方法:
对于当前的start,尝试找到最大的mask,使得该mask对应的块大小(即2^(32 - mask))使得start + block_size -1 <= end
.
同时,mask必须满足start的二进制中,后(32 - mask)位必须全为0,否则该子网的起始地址不是start,而是更小的地址。
例如,如果start是192.168.0.5
,而mask是24,那么子网的起始地址是192.168.0.0,而start不在该子网的起始地址,因此无法使用mask为24。因此,必须找到最大的mask,使得start是该子网的起始地址,即start的后(32 - mask)位必须是0。
因此,正确的步骤应该是:
计算从start开始,最大的后缀0的位数。这可以通过计算start的最低位1的位置,比如start的二进制中有n个后缀0,那么最大的mask是32 - n。此时,块大小是2^n。例如,start是192.168.0.0
(即…00000000),那么n是8,mask是24,块大小是256。
但如果start是192.168.0.128,二进制是…10000000,那么后缀0的位数是7,所以mask是25,块大小是128。此时,子网是192.168.0.128/25,广播地址是192.168.0.255。
但是,如果end是192.168.0.255,那么这个块可以包含。但是如果end是更小的值,比如192.168.0.200,那么需要调整mask,使得块大小不超过end - start +1。
因此,mask的确定应该是:
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)))
但是,计算lowBit的方式需要正确。例如,对于start的二进制,找到最右边1的位置。比如,start的二进制是…0001000,那么lowBit是3(从0开始计数),即2^3=8。所以块的大小是8,mask是32 -3 =29。
3.2 如何计算lowBit呢?
可以用位运算来找最右边的1的位置。例如,对于long类型的start,取值为start & -start,这会得到一个只有最右边1的数,然后计算log2。
例如,start & -start可以得到一个数,其中只有最右边的1被保留。例如,start是5(二进制101),start & -start的结果是1(二进制1),所以log2(1)=0,即lowBit=0。此时,块的大小是1,mask=32-0=32
.
如果是start是8(二进制1000),则start & -start=8
,log2(8)=3
,所以lowBit=3
,块的大小是8,mask=29
.
所以,lowBit = log2(start & -start)
. 但如何计算log2?可以用位运算,或者循环右移。
在Java中,可以使用Long.numberOfTrailingZeros()
方法,这个方法返回的是二进制中最低位1后面的0的个数。例如,对于8(1000),numberOfTrailingZeros
是3。此时,lowBit就是这个值,即块的大小是1 << lowBit。
但这个方法是否适用于所有情况?
假设start是0,那么start & -start是0,这可能有问题。但start在这里是介于起始和结束之间的,所以可能不会为0?
但起始IP可能为0,例如0.0.0.0到0.0.0.255。此时,lowBit是8,块的大小是256,mask是24.
所以,lowBit的计算应该是:
long diff = start & -start;
如果diff ==0
,那么lowBit=32
?或者如何处理?
或者,另一种方式,使用Long.numberOfTrailingZeros(startIpLong)
来获取最低位1后面的0的个数。例如,对于startIpLong=8(二进制1000),这个方法会返回3,因为末尾有3个0。此时,块的大小是1 << 3 =8.
所以,lowBit = Long.numberOfTrailingZeros(startIpLong | 0xFFFFFFFF00000000L)
? 不太确定,可能需要仔细测试。
或者,对于IPv4的32位,startIpLong是32位的数值,存储在long类型的高32位或者低32位?可能要看转换方式是否正确。例如,前面的转换函数是否正确地将IP转换为长整数,比如将第一个八位移到高位。
例如,IP地址a.b.c.d转换为长整数的方式是:a<<24 | b<<16 | c<<8 | d。这样,IPv4的32位被放在long的高32位还是低32位?
假设转换正确的话,那么对于192.168.0.5,转换为长整数是 (192 <<24) | (168<<16) | (0<<8) |5 = 3232235525。二进制表示为:
11000000 10101000 00000000 00000101
这里,最低位的1是在第0位(最后一个字节的5是00000101,最低位是1)。所以,Long.numberOfTrailingZeros(3232235525)
应该是0,因为最低位是1,后面没有0。所以块的大小是1,mask是32-0=32。
这样,每个块的大小是根据start的最低位1的位置决定的,但也要确保块的大小不超过剩余的地址空间(即end - start +1)。
3.3 综合起来,步骤如下
-
将
startIp
和endIp
转换为长整数s和e,确保s <=e。 -
初始化结果列表。
-
当
s <= e
时,循环:
a. 计算当前s的最低位1的位置,得到lowBit = Long.numberOfTrailingZeros(s)
.
b. 可能的块大小maxSize = 1 << lowBit
.
c. 但maxSize不能超过e - s +1
,所以实际块大小是Math.min(maxSize, e -s +1)
.
d. 这时候,需要找到最大的mask
,使得块大小为actualSize
。因为actualSize
必须是2的幂,所以mask = 32 - log2(actualSize)
.
e. 所以,mask = 32 - (int)(Math.log(actualSize)/Math.log(2))
.
f. 将CIDR
块添加到结果列表:IP/mask
.
g. s += actualSize
.
这样,每次循环处理一个块,直到覆盖整个范围。
例如,start=192.168.0.5
,end=192.168.0.7
:
s=3232235525 (0xC0A80005), e=3232235527
.
第一次循环:
lowBit = trailing zeros of s=5
(二进制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
.
第二次循环:
s=6
,二进制00000110,trailing zeros是1(因为最后两位是 10 →最低位是0?)
不,Long.numberOfTrailingZeros(6)
是1,因为6的二进制是110,末尾有一个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,循环结束。
所以,结果是两个CIDR块:/32和/31.
这似乎正确。
另一个例子,start=192.168.0.0
,end=192.168.3.255
:
s=3232235520
, e=3232238079
.
第一次循环:
lowBit=Long.numberOfTrailingZeros(s) →s=0xC0A80000
→二进制是11000000 10101000 00000000 00000000
→末尾有16个零?或者可能要看具体的转换方式。
如果IP地址转换为长整数的方式是每个八位作为高位,例如:
对于192.168.0.0,转换为:
192 <<24 | 168 <<16 | 0 <<8 |0 →即3232235520
,二进制是11000000 10101000 00000000 00000000
。
Long.numberOfTrailingZeros(3232235520)
→计算末尾的0的个数。3232235520
的二进制是:
11000000 10101000 00000000 00000000
→最后的16位都是0,所以 trailing zeros
是16.
所以,lowBit=16 →maxSize=1<<16=65536
.
但实际剩余的大小是 e -s +1 = 3232238079 -3232235520 +1=2560
.
所以actualSize=min(65536, 2560)=2560
.
但2560不是2的幂次方。这时候需要调整,找到最大的2的幂次方,不超过2560
.
所以,这里可能我的之前的思路有问题,即块的大小必须是2的幂次方,而实际剩余的大小可能不是,因此需要取最大可能的块,其大小是2的幂次方,并且不超过剩余的大小。
这时候,应该怎么做?
例如,在步骤c中,maxSize由lowBit决定,即可能的块大小是2^lowBit,但可能这个大小超过了剩余的大小,因此需要将actualSize设置为最大的2的幂次方,不超过剩余的大小。
或者,另一种方式,actualSize是最大的2的幂次方,使得actualSize <= remainingSize,并且actualSize <= maxSize(即由lowBit决定的块大小)。
例如,remainingSize
是2560,那么最大的2的幂次方是2048(2^11=2048),而maxSize由lowBit=16是65536,所以actualSize=2048.
此时,mask=32 -11=21.
这样,CIDR块是192.168.0.0/21?但是这样的话,该块的范围是192.168.0.0到192.168.7.255,但用户的结束IP是192.168.3.255,因此这样会超出范围。这说明我的逻辑有问题。
哦,这说明原来的算法可能存在问题。可能需要重新考虑如何确定块的大小。
正确的逻辑应该是,找到最大的mask,使得该子网的起始地址是s,并且该子网的结束地址 <=e.
也就是说,子网的掩码决定了块的大小是2^(32 - mask),所以我们需要找到最大的mask(即最小的块大小),使得 s + (2^(32 - mask)) -1 <=e.
同时,该mask必须满足s的后(32 - mask)位是0,也就是s是该子网的起始地址。
例如,对于s=192.168.0.0
,e=192.168.3.255
.
最大的mask是22,因为块大小是2(32-22)=210=1024,而s+1024-1=192.168.3.255
。这正是需要的。
所以,正确的CIDR
是192.168.0.0/22
.
因此,在这种情况下,算法应该返回一个CIDR块,而不是多个。那问题出在哪里?
块的大小应该同时满足:
-
该块的大小是2的幂次方,并且该块的起始地址是s。
-
该块的结束地址不超过e.
所以,块的大小应该是最大的可能的2的幂次方,满足 s + size -1 <=e,并且 size是最大的可能值,并且s mod size ==0.
3.4 如何计算这样的size?
算法步骤:
-
计算remaining = e - s +1.
-
初始size为最大的可能,即最大的2的幂次方 <= remaining.
-
同时,size必须满足 s & (size -1) ==0 (即s是该size的倍数)。
如果这两个条件满足,则size可以是该值,否则,需要将size减半,直到满足条件。
例如,对于s=192.168.0.0,e=192.168.3.255,remaining=1024.
最大的size是1024,且s mod 1024 ==0,所以可以使用size=1024,mask=22.
所以,正确的CIDR是/22.
那如何将此逻辑转化为代码?
实现步骤:
-
计算remaining = e -s +1.
-
计算
maxMask = 32 - (int)(Math.log(remaining)/Math.log(2))
→但这可能不正确,因为remaining可能不是2的幂次方。
或者,找到最大的mask,使得 2^(32 - mask) <= remaining,并且 s & (2^(32 -mask) -1) ==0.
正确的算法应该是:
循环处理,每次找到最大的size(2的幂次方),使得:
size <= remaining
并且 s + size -1 <=e
并且 s & (size -1) ==0 (即s是size的整数倍)
然后,mask=32 - log2(size).
然后,将CIDR块加入列表,s += size,remaining = e -s +1.
重复直到remaining=0.
现在,如何在代码中实现这一点?
步骤:
while s <=e:remaining = e -s +1;maxSize = 1 << 30; // 初始为较大的数,比如2^30size = 0;for (int shift = 0; shift <=32; shift++) {currentSize = 1 << shift;if (currentSize > remaining) break;if ((s & (currentSize -1)) ==0) {size = currentSize;}
}// 此时size是最大的满足条件的sizemask = 32 - log2(size) →即mask=32 - shift.add cidr: ipToString(s) + "/" + masks += size
但这样可能需要一个循环来找到最大的size。
或者,可以使用位运算来找到最大的可能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被调整为最大的满足条件的size.
例如,计算size的方法:
long size = Long.highestOneBit(remaining);
然后,循环检查 (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)
. 但如何计算log2(size)
?
可以用Long.numberOfLeadingZeros
来得到。例如,对于size=1024
,二进制是10000000000
,共11位,所以numberOfLeadingZeros
是64-11=53
。但Long.numberOfLeadingZeros(1024)
返回的是64 - 11 =53
?这可能需要调整。
或者,计算log2(size)可以使用位运算:
int logSize = 63 - Long.numberOfLeadingZeros(size);
例如,size=1024 →63 - 53(numberOfLeadingZeros(1024))
是10 →log2(1024)=10
.
所以,mask=32 - logSize
.
这样,例如size=1024 →mask=22
.
所以,综合以上分析,Java代码的大致结构可以是:
public static List<String> ipRangeToCidrs(String startIp, String endIp) {// 转换startIp和endIp为long s和eList<String> cidrs = new ArrayList<>();long s = ipToLong(startIp);long e = ipToLong(endIp);while (s <= e) {// 计算remaininglong remaining = e - s +1;// 计算最大的sizelong size = 1;int shift =0;// 找到最大的size=2^shift <= remaining,并且s & (size-1) ==0for (shift =0; (1L << (shift+1)) <= remaining; shift++) {if ((s & ( (1L << (shift+1)) -1 )) !=0) {break;}}size = 1L << shift;// 计算maskint mask = 32 - shift;// 添加CIDRcidrs.add(longToIp(s) + "/" + mask);// 更新ss += size;}return cidrs;
}
四、ipToLong
函数:
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;
}
这样,对于192.168.0.5
,结果是 (192 <<24) | (168 <<16) | (0 <<8) |5 = 3232235525L
.
五、longToIp
函数:
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
会被正确转换为192.168.0.5
.
六、编写主要的循环部分
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) {// 计算当前start的最低位1的位置,确定最大的可能的masklong maxSize = 1;int shift =0;// 找到最大的shift,使得 (1 << shift) <= remaining,并且 (start & ((1 << shift) -1)) ==0long remaining = end - start +1;// 初始shift为0,逐步增加,直到无法满足条件for (shift =0; (1L << (shift+1)) <= remaining; shift++) {// 检查如果增加shift后,是否start的后面的位都是0long 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函数应该包括:
-
ipToLong
和longToIp
的转换函数。 -
主函数处理循环,找到每个可能的
CIDR
块。 -
正确计算size和mask.
七、完整代码实现
以下是完整的Java 代码,并处理可能的异常和边界情况,可以将起始 IP 和结束 IP 转换为最精确的 CIDR 块列表。该方法通过位运算动态计算每个 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不能大于结束IP");}while (start <= end) {// 计算当前起始IP的最低位有效位(决定CIDR块的大小)int suffixZeros = Long.numberOfTrailingZeros(start);long maxSize = 1L << suffixZeros; // 可能的块大小(2^suffixZeros)long remaining = end - start + 1; // 剩余需要覆盖的IP数量// 实际块大小不能超过剩余范围long actualSize = Math.min(maxSize, remaining);// 计算CIDR的掩码位数(32 - log2(actualSize))int mask = 32 - (int) (Math.log(actualSize) / Math.log(2));// 将CIDR块加入列表cidrList.add(longToIp(start) + "/" + mask);// 移动到下一个块的起始IPstart += actualSize;}return cidrList;}// 将IP字符串转换为32位整数(用long存储)private static long ipToLong(String ip) {String[] octets = ip.split("\\.");if (octets.length != 4) {throw new IllegalArgumentException("无效的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位整数转换为IP字符串private static String longToIp(long ip) {return ((ip >> 24) & 0xFF) + "." +((ip >> 16) & 0xFF) + "." +((ip >> 8) & 0xFF) + "." +(ip & 0xFF);}// 测试用例public static void main(String[] args) {// 示例1:单个IP(/32)test("192.168.0.5", "192.168.0.5"); // ["192.168.0.5/32"]// 示例2:连续两个IP(/31)test("192.168.0.6", "192.168.0.7"); // ["192.168.0.6/31"]// 示例3:一个完整的C类子网(/24)test("192.168.0.0", "192.168.0.255"); // ["192.168.0.0/24"]// 示例4:跨子网的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");}
}
7.1 代码说明
-
核心逻辑
- 位运算计算:通过
Long.numberOfTrailingZeros
找到起始 IP 的最低位有效位,确定最大可能的 CIDR 块大小。 - 动态调整块大小:根据剩余 IP 范围 (
remaining
) 和起始 IP 的对齐要求,动态调整实际 CIDR 块大小。
- 位运算计算:通过
-
关键方法
ipToLong()
:将 IP 字符串转换为 32 位整数(用long
存储,避免符号问题)。longToIp()
:将整数转换回点分十进制格式。convertIpRangeToCidr()
:主方法,遍历 IP 范围生成 CIDR 列表。
-
测试案例
- 覆盖单 IP、连续 IP、完整子网和跨子网范围,验证不同场景的准确性。
7.2 执行示例
输入:
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
相关文章:
IP段转CIDR:原理Java实现
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编…...
工会考试知识点分享
工会考试涵盖工会基础知识、劳动法及相关法律法规、时政等内容,以下是一些常见的知识点分享: 工会基础知识 工会的性质与职能:工会是职工自愿结合的工人阶级的群众组织,基本职责是维护职工合法权益,同时还具有组织、…...
Java进阶——Stream流以及常用方法详解
本文详细介绍了 Java Stream 流的重要知识点。包括数据源与操作分离(不存储数据,不可复用)、惰性求值与短路优化;以及流的创建方式,如集合创建、数组 / 值创建、文件创建;然后介绍中间操作,像过…...
数据如何安全“过桥”?分类分级与风险评估,守护数据流通安全
信息化高速发展,数据已成为企业的核心资产,驱动着业务决策、创新与市场竞争力。随着数据开发利用不断深入,常态化的数据流通不仅促进了信息的快速传递与共享,还能帮助企业快速响应市场变化,把握商业机遇,实…...
Kubernetes LimitRange对于pod 的 update 事件会不会处理?
在 Kubernetes 中,LimitRange 是一个用于限制命名空间中 Pod 或容器资源使用的对象。它主要限制资源请求(requests)和资源限制(limits),如 CPU 和内存。LimitRange 影响的是 Pod 或容器的创建(c…...
服务器禁止操作汇总(Server Prohibits 0peration Summary)
服务器禁止操作汇总 一、禁忌操作TOP10 1. 直接断电关机 💥 血泪案例:某物流公司运维拔电源强制关机,导致数据库事务中断,20万订单状态丢失。 📌 技术解析: • 直接断电可能引发: ✅ 文件系统…...
Android Studio 新版本Gradle通过JitPack发布Maven仓库示例
发布本地仓库示例:https://blog.csdn.net/loutengyuan/article/details/145938967 以下是基于 Android Studio 24.2.2(Gradle 8.10.2 AGP 8.8.0 JDK17) 的通过JitPack发布Maven仓库示例,包含aar和jar的不同配置: 1.…...
Spring Boot 测试:单元、集成与契约测试全解析
一、Spring Boot 分层测试策略 Spring Boot 应用采用经典的分层架构,不同层级的功能模块对应不同的测试策略,以确保代码质量和系统稳定性。 Spring Boot 分层架构: Spring Boot分层架构 A[客户端] -->|HTTP 请求| B[Controller 层] …...
一个便捷的web截图库~
随着时间的发展,前端开发的范围越来越广,能够实现的功能也越来越多,要实现的功能也五花八门,今天就给大家介绍一个web截图库,让前端也能实现截图功能—— js-web-screen-shot js-web-screen-shot js-web-screen-shot 是一个基于 …...
【HTML— 快速入门】HTML 基础
准备工作 vscode下载 百度网盘 Subline Text 下载 Sublime Text下载 百度网盘 vscode 下载 Sublime Text 是一款轻量好用的文本编辑器,我们在写前端代码时,使用 Sublime Text 打开比使用记事本打开,得到的代码体验更好,比 vscode…...
github操作
在本地创建一个 Git 仓库并将其上传到 GitHub 的整个流程可以分为以下几个步骤。以下是详细的说明和对应的命令: 1. 安装 Git 确保你的系统已经安装了 Git。如果未安装,可以通过以下方式安装: Windows: 下载 Git for Windows 并安装。macOS…...
基于ArcGIS Pro、R、INVEST等多技术融合下生态系统服务权衡与协同动态分析实践应用
文章目录 前言第一章、生态系统服务第二章、平台基础一、ArcGIS Pro介绍二、R环境配置与基础操作 第三章、数据获取与预处理第四章、生态系统服务估算第五章、生态系统服务权衡与协同第六章、空间统计分析第七章、论文撰写与图表复现了解更多 ————————————————…...
Python Cookbook-2.18 从指定的搜索路径寻找文件
任务 给定一个搜索路径(一个描述目录信息的字符串),需要根据这个路径和请求的文件名找到第一个符合要求的文件。 解决方案 需要循环指定的搜索路径中的目录: import os def search_file(filename,search path,pathsepos.pathsep): """…...
遗传算法详解及在matlab中的使用
遗传算法分析 一 遗传算法概述1 算法概念2 基本特点3 启发式算法 二 原理与方法1 实现步骤1.1 个体编码1.2 种群初始化1.3 适应度计算1.4 选择运算1.5 交叉运算1.6 变异运算 2 总结 三 应用实例1 GA工具使用教程2 设置目标函数3 搜索最小值4 搜索最大值 一 遗传算法概述 本章简…...
智能AI替代专家系统(ES)、决策支持系统(DSS)?
文章目录 前言一、专家系统(ES)是什么?二、决策支持系统(DSS)是什么?1.决策支持系统定义2.决策系统的功能与特点3.决策支持系统的组成 三、专家系统(ES)与决策支持系统(D…...
活在AI原生时代的05后,开始用AI创业
大家好,我是Shelly,一个专注于输出AI工具和科技前沿内容的AI应用教练,体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具,拥抱AI时代的到来。 人工智能&AIGC术语100条 Shelly聊AI-重…...
【官方配图】win10/win11 安装cuda 和 cudnn
文章目录 参考资料1.安装cuda toolkit1. 下载安装包2.安装验证 2. 安装cudnn下载cudnn安装包安装cudnn安装后的配置 参考资料 官方nvidia安装cuda官方nvidia安装cudnn 1.安装cuda toolkit 1. 下载安装包 下载地址 https://developer.nvidia.com/cuda-downloads?target_osW…...
释放微软bing的力量:深度剖析其主要功能
在浩瀚无垠的互联网海洋中,搜索引擎就如同指南针,引领我们找到所需要的信息。微软必应凭借其一系列强大功能,在搜索引擎领域脱颖而出,成为极具竞争力的一员。在这篇博客文章中,我们将深入探讨微软必应的主要功能,这些功能使其独具特色,成为全球用户的得力工具。 1. 智能…...
【Nginx 】Nginx 部署前端 vue 项目
1. 项目打包 1.1 安装依赖 在项目部署之前,确保开发环境中已安装Node.js和npm,这是运行Vue项目的基础。通过执行npm install命令,可以安装项目所需的所有依赖。这一步是打包流程的前提,确保了后续编译的顺利进行。 根据npm的官…...
DO-254航空标准飞行器电机控制器设计注意事项
DO-254航空标准飞行器电机控制器设计注意事项 1.核心要求1.1 设计保证等级(DAL)划分1.2生命周期管理1.3验证与确认2.电机控制器硬件设计的关键注意事项2.1需求管理与可追溯性2.2冗余与容错设计2.3验证与确认策略2.4元器件选型与管理2.5环境适应性设计2.6文档与配置管理3.应用…...
C# Unity 唐老狮 No.3 模拟面试题
本文章不作任何商业用途 仅作学习与交流 安利唐老狮与其他老师合作的网站,内有大量免费资源和优质付费资源,我入门就是看唐老师的课程 打好坚实的基础非常非常重要: Unity课程 - 游习堂 - 唐老狮创立的游戏开发在线学习平台 - Powered By EduSoho 如果你发现了文章内特殊的字体…...
视频推拉流EasyDSS点播平台云端录像播放异常问题的排查与解决
EasyDSS视频直播点播平台是一个功能全面的系统,提供视频转码、点播、直播、视频推拉流以及H.265视频播放等一站式服务。该平台与RTMP高清摄像头配合使用,能够接收无人机设备的实时视频流,实现无人机视频推流直播和巡检等多种应用。 最近&…...
【分布式锁通关指南 05】通过redisson实现分布式锁
引言 在上个篇章中,我们通过redis手撸了一套分布式锁,但是最后也提到了它依然存在不完美的地方。那么有没有更简单和靠谱的实现方式。当然有,在本篇章中,我们将讲解如何使用redisson框架实现分布式锁以及理解它的源码。 什么是red…...
路劲家园大学:教育创新赋能社区人文生态建设
2025年2月10日至13日,路劲家园大学集训活动成功举办,众多教育领域学者与一线教师齐聚,通过专题研讨、教学展示、技术探索等多元形式,为家园大学注入全新活力,探索教育创新发展之路。 双院揭牌 构建社区美育新生态 集训…...
【前端进阶】10 掌握前端框架模板引擎的实现原理
前端框架模板引擎的实现原理 当用户对页面进行操作,页面内容更新,我们要实现的功能包括 如果使用前端框架 如果使用数据驱动的方式,还可以让逻辑与UI解耦的方式,提升代码的可维护性,其中的数据绑定、事件绑定等功能&a…...
Spring Boot 接口 JSON 序列化优化:忽略 Null 值的九种解决方案详解
一、针对特定接口null的处理: 方法一:使用 JsonInclude 注解 1.1 类级别:在接口返回的 DTO 类或字段 上添加 JsonInclude 注解,强制忽略 null 值: 类级别:所有字段为 null 时不返回 JsonInclude(Js…...
Golang 中如何实现一个强大的重试机制,来解决瞬态错误
文章精选推荐 1 JetBrains Ai assistant 编程工具让你的工作效率翻倍 2 Extra Icons:JetBrains IDE的图标增强神器 3 IDEA插件推荐-SequenceDiagram,自动生成时序图 4 BashSupport Pro 这个ides插件主要是用来干嘛的 ? 5 IDEA必装的插件&…...
冒泡排序算法优化
一 概述 冒泡排序是一种简单的交换排序算法,其核心思想是通过相邻元素比较和交换将最大元素逐步移动到数组末尾。 二、基础冒泡排序 void bubbleSort(int arr[], int n) { for (int i = 0; i < n-1; i++) { for (int j = 0; j < n-i-1; j++) { if…...
2025年Linux主力系统选择指南:基于最新生态的深度解析(附2025年发行版对比速查表)
Linux发行版生态在2025年持续演进,既有经典系统的迭代升级,也有新兴项目的崛起。本文结合最新行业动态,从个人用户到企业场景,梳理主力系统选择策略,助你找到最适合的Linux发行版。 一、新手友好型:平滑过渡…...
聊聊大数据测试开展方向有哪些?
目录 一、功能性测试与验证 二、数据的更新实时性测试 三、数据响应的及时性测试 四、算法的效果验证 五、AI算法系统的线上稳定性保证 大数据测试实施建议 大数据测试和传统软件测试有什么不同呢?可能涉及数据量大、多样性、处理速度这些特点。然后ÿ…...
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_init_cycle 函数 - 详解(11)
详解(11) 初始化配置解析上下文 senv environ;ngx_memzero(&conf, sizeof(ngx_conf_t));/* STUB: init array ? */conf.args ngx_array_create(pool, 10, sizeof(ngx_str_t));if (conf.args NULL) {ngx_destroy_pool(pool);return NULL;}conf.te…...
AI与机器学习、深度学习在气候变化预测中的应用
全球气候变化是现代社会面临的最重要的环境挑战之一,影响了气温、降水、海平面、农业、生态系统等多个方面。气候变化的驱动因素主要包括温室气体排放、气溶胶浓度、火灾频发、海冰融化、叶绿素变化、农业变化和生态环境变化等。这些因素在全球范围内交互作用&#…...
湖仓一体概述
湖仓一体之前,数据分析经历了数据库、数据仓库和数据湖分析三个时代。 首先是数据库,它是一个最基础的概念,主要负责联机事务处理,也提供基本的数据分析能力。 随着数据量的增长,出现了数据仓库,它存储的是…...
Go红队开发—语法补充
文章目录 错误控制使用自定义错误类型错误包装errors.Is 和 errors.Aspanic捕获、recover 、defer错误控制练习 接口结构体实现接口基本类型实现接口切片实现接口 接口练习Embed嵌入文件 之前有师傅问这个系列好像跟红队没啥关系,前几期确实没啥关系,因为…...
【VSCode】VSCode下载安装与配置极简描述
VSCode 参考网址:[Visual Studio Code Guide | GZTime’s Blog]. 下载安装 下载地址:Download Visual Studio Code - Mac, Linux, Windows. 注:推荐不更改安装位置,并且在附加任务中“其他”中的四项全部勾选,即将用…...
揭开人工智能中 Tokens 的神秘面纱
揭开人工智能中 Tokens 的神秘面纱 在人工智能,尤其是自然语言处理(NLP)领域,"tokens" 是一个频繁出现且至关重要的概念。对于理解语言模型如何处理和理解人类语言,tokens 起着基础性的作用。那么ÿ…...
Node.js安装与学习的简单记录
1. 下载与安装 参考: 2024最新版Node.js下载安装及环境配置教程【保姆级】 Node.js中文网 选择长期维护版: 18.19.0,Windows 安装包 (.msi) 64位。 安装选项都默认,安装路径可以改一下。 查看node版本:node -v v18.19.0 查看npm版…...
Spring也能接入Deepseek?
引言 最近DeepSeek可谓风光无限,AI可谓是目前互联网最火热的几个名词,我也一直在关注他的发展,从以前的人工智障,到chatGPT的高不可攀(价格太贵),再到DeepSeek的横空出世,才看到了A…...
STM32 物联网智能家居 (七) 设备子系统--风扇控制
STM32 物联网智能家居 (七) 设备子系统–风扇控制 一、概述 下面我们来讲解设备子系统中的风扇控制,这是我们设备子系统中的最后一章,相信前面大家一家掌握了这种架构分层的编程思想,后续会很容易将程序进行扩展和开发。 上一节我们介绍了OLED屏幕的编程思想,有很多小伙…...
MySQL—Keepalived+MySQL双主复制实现MySQL高可用
Keepalived原理: Keepalived 的原理主要基于虚拟路由冗余协议(VRRP,Virtual Router Redundancy Protocol)、健康检查机制和负载均衡机制,以下为你详细介绍: VRRP 协议实现高可用:VRRP 是 Keep…...
CSS 使用white-space属性换行
一、white-space属性的常见值 * 原本格式: 1、white-space:normal 默认值,空格和换行符会被忽略过滤掉;宽度不够时文本会自动换行 * 宽度足够时,normal 处理后的格式 * 宽度不够时, normal 处理后的格式 2、white-spa…...
【数据结构第十六节】实现链式结构二叉树(详细递归图解—呕心沥血版!)
必须有为成功付出代价的决心,然后想办法付出这个代价。云边有个稻草人-CSDN博客 这节课挺抽象(苦笑),没事,我会帮你!干就完了! (目录在路上) 正文开始—— 引言 用链表…...
mysqldump 参数详解
mysqldump 是一个用于备份 MySQL 数据库的工具。它可以生成一组 SQL 语句,这些语句可以用来重现原始数据库对象定义和表数据。以下是一些常用的 mysqldump 参数及其详细解释: 常用参数 基本参数 --host=host_name, -h host_name: 指定 MySQL 数据库主机地址,默认为 localh…...
Vue-Flow绘制流程图(Vue3+ElementPlus+TS)简单案例
本文是vue3Elementplusts框架编写的简单可拖拽绘制案例。 1.效果图: 2.Index.vue主代码: <script lang"ts" setup> import { ref, markRaw } from "vue"; import {VueFlow,useVueFlow,MarkerType,type Node,type Edge } fro…...
【11】RUST使用cargo组织crate
文章目录 使用cargo组织crate重导出编译文档生成测试 cargo组织工作空间 TODOcrate.io账号 TODO暂时不看发布crate 使用cargo组织crate 重导出 在模块顶部使用pub use self::重导出,方便使用模块时候直接使用use mod_X::xxx。从而隐藏crate内部模块的结构。方便向…...
开放标准(RFC 7519):JSON Web Token (JWT)
开放标准:JSON Web Token 前言基本使用整合Shiro登录自定义JWT认证过滤器配置Config自定义凭证匹配规则接口验证权限控制禁用session缓存的使用登录退出单用户登录Token刷新双Token方案单Token方案 前言 JSON Web Token (JWT) 是一种开放标准…...
Linux上用C++和GCC开发程序实现不同MySQL实例下单个Schema之间的稳定高效的数据迁移
设计一个在Linux上运行的GCC C程序,同时连接两个不同的MySQL实例,两个实例中分别有两个Schema的表结构完全相同,复制一个实例中一个Schema里的所有表的数据到另一个实例中一个Schema里,使用以下快速高效的方法,加入异常…...
【Windows】Windows常用命令
目录 文件和目录相关命令系统信息查看命令网络相关命令进程管理命令磁盘管理命令用户和权限管理命令计划任务和脚本命令其他常用命令1. 文件和目录相关命令 命令作用示例cd切换目录cd C:\Usersdir列出目录内容dirmkdir创建新目录mkdir NewFolderrmdir删除空目录rmdir OldFolder…...
趣讲TCP三次握手
一、TCP三次握手简介 TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在TCP连接中,只有两方进行通信,它使用校验和、确认和重传机制来保证数据的可靠传输。…...
vue3中的标签属性中的Ref
用在普通 DOM 标签上,获取的是 DOM 节点: 当你在一个普通的 HTML 标签(例如 <div>、<input> 等)上使用 ref 属性时,ref 会返回该 DOM 元素的直接引用。这使得你可以在 JavaScript 代码中方便地访问和操作…...