当前位置: 首页 > article >正文

IP段转CIDR:原理Java实现

🧑 博主简介:CSDN博客专家历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程高并发设计Springboot和微服务,熟悉LinuxESXI虚拟化以及云原生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的幂次方时。

一、需要考虑以下几点

  1. 将IP地址转换为整数:IPv4地址是32位的,可以将每个部分转换为二进制,然后组合成一个长整数,这样便于计算。例如,192.168.1.0转换为整数是3232235776。

  2. 确定起始和结束IP的整数形式:需要将输入的字符串拆分四个部分,每个部分转换为整数,并进行位移组合成一个长整型。

  3. 计算CIDR块:从起始地址开始,找到最大的前缀长度(即最小的子网),使得该子网包含在起始和结束范围内。然后,将起始地址递增到下一个子网的起始地址,重复这个过程直到覆盖整个范围。

这里的关键步骤是如何找到每个可能的CIDR块。例如,假设起始IP是192.168.0.0,结束IP是192.168.3.255。这个范围可以被分解为192.168.0.0/23或者192.168.0.0/24192.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代码,需要以下步骤:

  1. 编写一个方法将IP字符串转换为长整数

  2. 编写一个方法将长整数转换回IP字符串

  3. 编写方法计算两个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=8log2(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 综合起来,步骤如下

  1. startIpendIp转换为长整数s和e,确保s <=e。

  2. 初始化结果列表。

  3. 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.5end=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.0end=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.0e=192.168.3.255.

最大的mask是22,因为块大小是2(32-22)=210=1024,而s+1024-1=192.168.3.255。这正是需要的。

所以,正确的CIDR192.168.0.0/22.

因此,在这种情况下,算法应该返回一个CIDR块,而不是多个。那问题出在哪里?

块的大小应该同时满足:

  1. 该块的大小是2的幂次方,并且该块的起始地址是s。

  2. 该块的结束地址不超过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.

那如何将此逻辑转化为代码?

实现步骤:

  1. 计算remaining = e -s +1.

  2. 计算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位,所以numberOfLeadingZeros64-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函数应该包括:

  • ipToLonglongToIp的转换函数。

  • 主函数处理循环,找到每个可能的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 代码说明

  1. 核心逻辑

    • 位运算计算:通过 Long.numberOfTrailingZeros 找到起始 IP 的最低位有效位,确定最大可能的 CIDR 块大小。
    • 动态调整块大小:根据剩余 IP 范围 (remaining) 和起始 IP 的对齐要求,动态调整实际 CIDR 块大小。
  2. 关键方法

    • ipToLong():将 IP 字符串转换为 32 位整数(用 long 存储,避免符号问题)。
    • longToIp():将整数转换回点分十进制格式。
    • convertIpRangeToCidr():主方法,遍历 IP 范围生成 CIDR 列表。
  3. 测试案例

    • 覆盖单 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实现

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…...

[STM32]从零开始的STM32 DEBUG问题讲解及解决办法

一、前言 最近也是重装了一次keil&#xff0c;想着也是重装了&#xff0c;也是去官网下载了一个5.41的最新版&#xff0c;在安装和配置编译器和别的版本keil都没太大的区别&#xff0c;但是在调试时&#xff0c;遇到问题了&#xff0c;在我Debug的System Viewer窗口中没有GPIO&…...

MySQL当中的Lock

1. 总览锁的类型 锁的类型&#xff1a; 锁类型 符号/缩写 描述 全局锁 FTWRL 锁定整个数据库&#xff08;FLUSH TABLES WITH READ LOCK&#xff09;&#xff0c;用于全库备份。 表级锁 - 表锁 S/X LOCK TABLES ... READ&#xff08;共享锁&#xff09;或 WRITE&#…...

electron-builder打包时github包下载失败【解决办法】

各位朋友们&#xff0c;在使用electron开发时&#xff0c;选择了electron-builder作为编译打包工具时&#xff0c;是否经常遇到无法从github上下载依赖包问题&#xff0c;如下报错&#xff1a; Get "https://github.com/electron/electron/releases/download/v6.1.12/ele…...

【免费】YOLO[笑容]目标检测全过程(yolo环境配置+labelimg数据集标注+目标检测训练测试)

一、yolo环境配置 这篇帖子是我试过的&#xff0c;非常全&#xff0c;很详细【cudaanacondapytorchyolo(ultralytics)】 yolo环境配置 二、labelimg数据集标注 可以参考下面的帖子&#xff0c;不过可能会出现闪退的问题&#xff0c;安装我的流程来吧 2.1 labelimg安装 label…...

服务器禁止操作汇总(Server Prohibits 0peration Summary)

服务器禁止操作汇总 一、禁忌操作TOP10 1. 直接断电关机 &#x1f4a5; 血泪案例&#xff1a;某物流公司运维拔电源强制关机&#xff0c;导致数据库事务中断&#xff0c;20万订单状态丢失。 &#x1f4cc; 技术解析&#xff1a; • 直接断电可能引发&#xff1a; ✅ 文件系统…...

UE5 Slate类的基础创建

创建一个slate类的基础代码 #pragma onceclass SCustomDetailPlane : public SCompoundWidget {SLATE_BEGIN_ARGS(SCustomDetailPlane){}SLATE_END_ARGS()public:SCustomDetailPlane();~SCustomDetailPlane();void Construct(const FArguments& InArgs);};***************…...

springboot2.7.18升级springboot3.3.0遇到的坑

druid的警告&#xff0c;警告如下&#xff1a; 运行警告2025-02-28T09:20:31.28508:00 WARN 18800 --- [ restartedMain] trationDelegate$BeanPostProcessorChecker : Bean com.alibaba.druid.spring.boot3.autoconfigure.stat.DruidSpringAopConfiguration of type [com.a…...

服务器IPMI用户名、密码批量检查

背景 大规模服务器部署的时候&#xff0c;少不了较多的网管和监测平台&#xff0c;这些平台会去监控服务器的性能、硬件等指标参数&#xff0c;为了便于管理和控制&#xff0c;则需要给服务器IPMI带外管理添加较多的用户&#xff0c;这就需要对较多的服务器检查所对应的IPMI用…...

JAVA面试_进阶部分_netty面试题

1.BIO、NIO 和 AIO 的区别&#xff1f; BIO&#xff1a;一个连接一个线程&#xff0c;客户端有连接请求时服务器端就需要启动一个线程进行处理。线程开销大。 伪异步 IO&#xff1a;将请求连接放入线程池&#xff0c;一对多&#xff0c;但线程还是很宝贵的资源。 NIO&#x…...

小红书湖仓架构的跃迁之路

作者&#xff1a;李鹏霖(丁典)&#xff0c;小红书-研发工程师&#xff0c;StarRocks Contributor & Apache Impala Committer 本文整理自小红书工程师在 StarRocks 年度峰会上的分享&#xff0c;介绍了小红书自助分析平台中&#xff0c;StarRocks 与 Iceberg 结合后&#x…...

C++-第十七章:包装器

目录 第一节&#xff1a;std::function 第二节&#xff1a;std::bind 2-1.基本介绍 2-2.调整顺序(不常用) 2-3.调整个数 2-4.std::bind与std::function 下期预告&#xff1a; C中有3种可调用对象&#xff1a;函数指针、仿函数对象、lambda函数&#xff0c;经过包装器包装后屏…...

如何判断邮件列表中邮箱地址的有效性?

判断邮件列表中邮箱地址的有效性&#xff0c;对于提高邮件送达率、避免资源浪费和维护发件人信誉至关重要。以下是一些实用的判断方法&#xff1a; 一、使用专业的邮箱验证工具 市面上有许多专业的邮箱验证工具&#xff0c;如 Geeksend邮箱验证工具 等。这些工具通过与邮件服…...

翻译: 深入分析LLMs like ChatGPT 二

监督微调&#xff08;SFT&#xff09; 使用人工标注的对话数据集&#xff08;如1M条"用户-助手"对话&#xff09;继续训练模型。 标注员遵循指导原则编写理想回答&#xff0c;使模型学习助手的回应风格。 示例对话格式&#xff1a; [系统] 你是一个有帮助的AI助手……...

conda怎么迁移之前下载的环境包,把python从3.9升级到3.10

克隆旧环境&#xff08;保留旧环境作为备份&#xff09; conda create -n cloned_env --clone old_env 在克隆环境中直接升级 Python conda activate cloned_env conda install python3.10 升级 Python 后出现 所有包导入失败 的问题&#xff0c;通常是因为依赖包与新 Pyth…...

k8s之pod的调度之污点与容忍污点,什么是污点? 如何容忍污点

在 Kubernetes 中&#xff0c;污点&#xff08;Taint&#xff09; 和 容忍&#xff08;Toleration&#xff09; 是用于控制 Pod 调度到特定节点的重要机制。污点允许节点拒绝某些 Pod 的调度&#xff0c;而容忍则允许 Pod 忽略节点的污点&#xff0c;从而调度到特定节点上。 1.…...

Linux切换Python版本

1、更新apt sudo apt update2、查询python安装路径 which python 或者which python33、查询安装版本 # 查看所有以 "python" 开头的命令&#xff08;包括版本号&#xff09; ls -l 安装路径* 例如 ls -l /usr/bin/python*4、修改软连接 udo unlink /usr/bin/pyt…...

TCP的三次握手与四次挥手:建立与终止连接的关键步骤

引言 ‌TCP&#xff08;传输控制协议&#xff09;工作在OSI模型的传输层‌。OSI模型将计算机网络功能划分为七个层级&#xff0c;从底层到顶层依次是&#xff1a;物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。传输层负责在网络节点之间提供可靠的端到端通信&a…...

2025计算机考研复试资料(附:网课+历年复试真题+140所高校真题+机试)

目录 2025 计算机考研复试经验全攻略&#xff0c;附超全资源&#x1f381; &#xff08;一&#xff09;网课资源 &#xff08;二&#xff09;历年复试真题 &#xff08;三&#xff09;140 所高校真题 二、专业知识复习篇 &#xff08;一&#xff09;复试专业课程 二&…...

Milvus高性能向量数据库与大模型结合

Milvus | 高性能向量数据库&#xff0c;为规模而构建Milvus 是一个为 GenAI 应用构建的开源向量数据库。使用 pip 安装&#xff0c;执行高速搜索&#xff0c;并扩展到数十亿个向量。https://milvus.io/zh Milvus 是什么&#xff1f; Milvus 是一种高性能、高扩展性的向量数据…...

【Groovy】流程控制

1 选择结构 Groovy 中选择结构主要包含 if -else、switch 语句&#xff0c;并且可以返回结果。 1.1 if-else def score 85 if (score > 90) {println("优秀") } else if (score > 80) {println("良好") } else if (score > 60) {println("…...

腾讯游戏完成架构调整 IEG新设五大产品事业部

易采游戏网2月28日独家消息&#xff1a;继1月份腾讯天美工作室群完成内部组织架构调整后&#xff0c;腾讯旗下互动娱乐事业群&#xff08;IEG&#xff09;再次宣布对组织架构进行优化调整。此次调整的核心在于新设立了五大产品事业部&#xff0c;包括体育产品部、音舞产品部、V…...

达梦数据库系列之安装及Mysql数据迁移

达梦数据库系列之安装及Mysql数据迁移 1. 达梦数据库1.1 简介1.2 Docker安装达梦1.2.1 默认密码查询1.2.2 docker启动指定密码 1.3 达梦数据库连接工具1.3.1 快捷键 2 Mysql数据库迁移至达梦2.1 使用SQLark进行数据迁移 1. 达梦数据库 1.1 简介 DM8是达梦公司在总结DM系列产品…...

什么是 MGX:MetaGPT

什么是 MGX:MetaGPT MetaGPT是由思码逸(OpenDILab)团队开发的一款专注于生成式AI驱动的软件开发框架,MGX可能是其衍生或升级的相关成果,它创新性地将大语言模型引入软件开发流程,模拟人类软件团队的协作方式,能让用户通过自然语言描述需求,即可自动生成完整的软件项目,…...

java jar包内的jar包如何打补丁

问题描述&#xff1a; 主包&#xff1a;hisca.jar&#xff0c;解压后 BOOT-INFO/lib下有其他jar包 因为一个小bug&#xff0c;需要修改这个hisca包下BOOT-INF/lib下的子jar包service-hisca-impl-1.0.0.jar中的一个service类及xml文件 操作步骤&#xff1a; 1、主包jar -xvf …...

一个借助ai分析市场交易数据的流程方法

回答和代码借助 AI 生成&#xff0c;仅供参考&#xff0c;不构成任何专业建议。 如有投资损失请自负盈亏。 一个提取比特币的不同周期数据&#xff0c;并进行文本的初步分析的程序。 用途&#xff1a;把文本提供给ai&#xff0c;进行深度思考&#xff0c;从而达到一个相对比较…...

安装electron 提示RequestError: certificate has expired

最近需要开发electron&#xff0c;遇到了一个生产问题&#xff0c;使用了很多办法都不生效。 现在记录下具体情况 一执行 yarn add electron 就开始报错 [2/4] &#x1f69a; Fetching packages... [3/4] &#x1f517; Linking dependencies... [4/4] &#x1f528; B…...

Flutter状态管理框架GetX最新版详解与实践指南

一、GetX框架概述 GetX是Flutter生态中轻量级、高性能的全能开发框架&#xff0c;集成了状态管理、路由导航、依赖注入等核心功能&#xff0c;同时提供国际化、主题切换等实用工具。其优势在于代码简洁性&#xff08;减少模板代码约70%&#xff09;和高性能&#xff08;基于观…...

详细对比所有开源许可及其不同版本

qwen 以下是基于知识库内容对常见开源许可证及其不同版本的详细对比&#xff0c;重点突出版本间差异和核心条款变化&#xff1a; 1. GPL&#xff08;GNU General Public License&#xff09; 版本: v2 vs v3 GPLv2&#xff08;1991&#xff09;: 要求衍生代码开源&#xff0c…...

【企业场景】上线的项目如何进行限流

一、常见的四种速率限流算法 对于限流&#xff0c;最为直接的就是速率限流了 固定窗口算法 比如 10r/s 就是把时间线分为 1s 一段&#xff0c;也就是周期为 1s&#xff0c;对一个时间段的请求进行计数&#xff0c;超过 10 则舍弃&#xff0c;未超过则直接处理经过 1s 后&…...