Nicksxs's Blog

What hurts more, the pain of hard work or the pain of regret?

将一个小点,在局域网中包括类似于homelab的或者仅仅只是搭一个用来当实验环境的,在局域网内机器比较少的时候大部分DHCP会给分配相同的ip,但是这不是一定的,当机器比较多,并且上下线比较频繁时就会出现ip地址变动,这对于一些使用场景比较讨厌,所以我想把ip固定下来,先简单引用下DHCP的原理,他分为下面几个阶段

DHCP发现(DISCOVER)

client在物理子网上发送广播来寻找可用的服务器。网络管理员可以配置一个本地路由来转发DHCP包给另一个子网上的DHCP服务器。该client实现生成一个目的地址为255.255.255.255或者一个子网广播地址的UDP包。

客户也可以申请它使用的最后一个IP地址(在下面的例子里为192.168.1.100)。如果该客户所在的网络中此IP仍然可用,服务器就可以准许该申请。否则,就要看该服务器是授权的还是非授权的。授权服务器会拒绝请求,使得客户立刻申请一个新的IP。非授权服务器仅仅忽略掉请求,导致一个客户端请求的超时,于是客户端就会放弃此请求而去申请一个新的IP地址。

DHCP提供(OFFER)

当DHCP服务器收到一个来自客户的IP租约请求时,它会提供一个IP租约。DHCP为客户保留一个IP地址,然后通过网络单播一个DHCPOFFER消息给客户。该消息包含客户的MAC地址、服务器提供的IP地址、子网掩码、租期以及提供IP的DHCP服务器的IP。

服务器基于在CHADDR字段指定的客户硬件地址来检查配置。这里的服务器,192.168.1.1,将IP地址指定于YIADDR字段。

DHCP请求(REQUEST)

当客户PC收到一个IP租约提供时,它必须告诉所有其他的DHCP服务器它已经接受了一个租约提供。因此,该客户会发送一个DHCPREQUEST消息,其中包含提供租约的服务器的IP。当其他DHCP服务器收到了该消息后,它们会收回所有可能已提供给该客户的租约。然后它们把曾经给该客户保留的那个地址重新放回到可用地址池中,这样,它们就可以为其他计算机分配这个地址。任意数量的DHCP服务器都可以响应同一个IP租约请求,但是每一个客户网卡只能接受一个租约提供。

DHCP确认(Acknowledge,ACK)

当DHCP服务器收到来自客户的REQUEST消息后,它就开始了配置过程的最后阶段。这个响应阶段包括发送一个DHCPACK包给客户。这个包包含租期和客户可能请求的其他所有配置信息。这时候,TCP/IP配置过程就完成了。

该服务器响应请求并发送响应给客户。整个系统期望客户来根据选项来配置其网卡。

DHCP释放(RELEASE)

客户端向DHCP服务器发送一个请求以释放DHCP资源,并注销其IP地址。鉴于客户端更多的时候并不清楚何时用户会将其从网络中移除,此协议不会托管“DHCP释放的发送”。

DHCP NAK

服务器回复客户,客户要求的IP不能被分配。

Ubuntu

而对于Ubuntu,现在较新版本的设置方式也有点变化,比如我用的是22.04

1
cd /etc/netplan/

先去这个目录下查看配置文件
我们要找的是这个 00-installer-config.yaml 文件

原本的是长这样

默认是通过dhcp分配ip
我们要改成静态ip

1
2
3
4
5
6
7
8
9
10
11
12
network:
renderer: networkd
ethernets:
ens33:
addresses:
- 192.168.1.xx/24
nameservers:
addresses: [114.114.114.114, 233.5.5.5]
routes:
- to: default
via: 192.168.1.1
version: 2

解释一下 ethernets中的ens33表示我们的网卡接口的标识
可以通过 ip addr 查看

第一个是本地回环,第二个可以看下是否跟内网ip对应上,如果是的话就这个ens33
然后就是addresses填的地址,就是我们想要固定的ip地址,记得得是自己局域网网段的
上面只是个示例,然后是nameservers里的是dns服务器,也是按需填写
这里的114.114.114.114跟233.5.5.5分别是电信跟阿里云的,仅供参考
然后是routes就是网关地址,按自己的环境填写,网络没有任何特殊处理的一般就是路由器
接下去就是应用这个配置

1
sudo netplan apply

这样ip配置就生效了
可以通过
ip addr show ens33查看

之前自定义部署的derper在Mac端和Windows使用时没啥问题,但是在我想作为网关的Armbian小机器上一直会报 “x509: certificate signed by unknown authority”
错误,原因在于之前在通过acme生成证书以后我是直接把cer证书重命名成crt就映射到derper的docker目录里,对于Mac和Windows客户端应该是有兼容逻辑,而实际使用需要完整的证书去识别认证机构,否则就会被认为是自定义证书,在一些常规的证书认证逻辑里就会报上面的问题
这边需要先把生成的 fullchain.cer 完整证书转成crt格式的

1
openssl x509 -inform PEM -in fullchain.cer -out derper.demo.com.crt

这边是PEM格式的证书,如果是DER的话就改一下

1
openssl x509 -inform DER -in certificate.cer -out certificate.crt

转换完成后就重新启动下docker
这里的原因主要是fullchain.cer是包含了完整的证书链,而ca.cer只是包含用户证书,所以需要替换一下,这算是个小问题

上次分享了rustdesk如何私有化部署,但是在实际使用中存在一个小问题,就是如果被连接端是mac的话,在mac进入超时自动锁定或者被主动锁定之后,连接就会进入一个“已连接,等待画面传输”的状态,但实际后续并不会传输画面,而是继续卡在这个无法实际连接的状态

这个问题目前最新版的rustdesk还没有很好的解决,不过可以通过一个小技巧来解决,只是会有一个限制
就是需要能够使用vnc连接这个被连接端的mac或者ssh连接
第一种是如果能用vnc连接的话,就先连接,然后稍微动下鼠标,然后rustdesk就能获取画面进行下一步操作
第二种是通过ssh连接到mac以后,通过一个mac下的小命令 caffeinate 这个命令是mac自带的,可以防止mac
进入睡眠状态,具体命令如下

1
caffeinate -u -t 2

-u: 表示用户处于活动状态,此参数将打开屏幕并禁止屏幕进入空闲休眠。

-d:禁止屏幕休眠。

-i:禁止系统空闲休眠。

-m:禁止硬盘进入休眠。

-s:禁止系统进入睡眠状态,此参数仅在插上电源的时候才有效。

-t:指定命令有效的超时值,以秒为单位。
这样我们就能从rustdesk获得画面进行操作了,希望后面rustdesk能解决掉这个问题,因为这两种方式也不是很完善,如果只有纯远程的话就没办法了

最近因为利用rustdesk远程操作诈骗太多,所以rustdesk官方直接关闭了国内的公有服务,相对其他比如teamviewer来说,rustdesk还是个比较不错的选择,性能考量也过得去,相对来说在双方都是mac的情况下,不付费的体验来说,比自带的vnc好了很多,vnc目测是直接全量传实时画面数据,带宽占用大,体验差。正巧最近有需求就使用了rustdesk所以记录下部署过程
我使用的较早先版本的部署方式,
首先需要一台有外网ip的服务器,并且需要使用到以下几个端口,如果使用云服务器,需要在控制台开启这些端口
核心端口:

1
2
TCP 21114-21119
UDP 21116

测试有没有开启可以使用这个工具网站 CanYouSeeMe.org ,或者用另一台机器telnet
需要部署的服务有两个
hbbs - RustDesk ID/Rendezvous server id服务
hbbr - RustDesk Relay server 中继服务
这里使用docker来部署,比较简单,先拉取镜像,没错就一个

1
sudo docker image pull rustdesk/rustdesk-server

这边建议创建个rustdesk目录,进入目录后再启动下面的服务
先启动hbbs

1
sudo docker run --name hbbs -p 21115:21115 -p 21116:21116 -p 21116:21116/udp -p 21118:21118 -v `pwd`:/root -td --net=host rustdesk/rustdesk-server hbbs

使用 --net=host 是为了让server拿到连接进入的真实来源ip
然后再启动hbbr

1
sudo docker run --name hbbr -p 21117:21117 -p 21119:21119 -v `pwd`:/root -td --net=host rustdesk/rustdesk-server hbbr

接下来进到刚才建的目录,会看到有个一对公私钥,把公钥内容保存下来
然后就是客户端登录了,以mac为例

点击这边三点,再点击修改中继设置

在弹出窗口

ID服务器跟中继服务器框填入前面部署服务的服务器外网ip,在Key框里把刚才保存的公钥填进去
每个需要连接的客户端都需要这么设置,然后就可以使用自己的服务器作为ID跟中继服务器了,不会受官方关停国内服务影响

这题的题目介绍有一定的误导性,没有把字典序说明白,虽然的确是常规意义的字典序,但是题目介绍给的序列顺序反而会让人觉得那样不对
这里我们逐步深入地来讲这个题,也类似于找规律
注意:这里的递减递增序列都是从前往后看的
字典序可以就当查字典,或者默认的字符串排序方式,就是对于一个序列,逐个字符对比,同一位的相同再对比下一位的,例如 11在12前面,12在13前面,
那对于同一组字符或者像题中的数字,以最简单的12来分析,因为这个就两种排列,12跟21,按字典序排列,12在前,21在后
通过这个仔细点也能发现,或者再举个例子123,这个排列就会多一些了

1
2
3
4
5
6
123
132
213
231
312
321

一共有这么几种,不过举这个例子是为了更具体的发现规律,对于第一个数字相同的情况,比如123跟132,是132在后,发现规律没,递增序列是最小的,递减序列是最大的,因为2个数字 只有两种排列,扩展到3个数字,也可以把它拆分开,同样是第一个数字是1,后面两个数字就又回到了前面12序列的情况,
所以就可以把问题分成两部分来解决,首先从后往前找到递减序列
通过例子来讲

1
1 4 6 5 3 2

为什么要找到递减序列,因为递减序列已经是最大的了,要做调整要在这个之前调整,并且要找的是下一个序列,所以要是最接近的那个,否则我找到的可能就是下N个了,这个有点类似于数学里进位的意思,比如十九,九已经是个位里最大的了,找下一个比它大的只能是前面一位加一,但是加一以后需要九变到这一位的最小,也就是零。
对于上面的例子,递减序列就是6532,第一个非递减的是4,我要找下一个就是要比当前这个大,那我就去后面找比4大的,至于为什么能找到,因为这是递减序列边界之外的,说明肯定有比它大的,那要找哪个呢,要找比4大的数字里最小的,这样才是最近的下一个序列,并且因为后面是递减序列,所以可以直接从后往前找第一个
也就是会找到 5
找到 5 之后就做交换

1
1 5 6 4 3 2

这样是不是就行了呢,不行,因为这不是下一个,而是下N个,当4变成5之后,这个序列不管5后面的数字怎么变都是比 146532 大的,但是要找的是下一个,最接近的那个,则需要后面的序列变成最小,也就是变成递增序列,也就是

1
1 5 2 3 4 6

这里有两种例外情况,或者严格来说是一种
第一种是本身整个就是递减序列了,这在题中说了,下一个就是从头再开始,那就也很简单整个排序成递增的就行了
第二种是本身就是个完全递增的,那其实就交换末两位,但这种跟上面的可以糅合在一起,比如就是

1
1 2 3 4 5 6

单个6构不成递增或递减,找到的第一个非递减的数字就是 5 ,那就是在 5 后面找一个比它大的做交换,这时只有 6 ,所以就 5 跟 6 交换下,然后单个 5 做排序也等于不用做
再结合代码来看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void nextPermutation(int[] nums) {
int n = nums.length, i = n - 2, j = n - 1;
// 这个while循环是为了从后往前找到递减序列的分界点
// 循环完成后i就是递减序列左边的第一个数字位置
while (i >= 0 && nums[i] >= nums[i + 1]) --i;
if (i >= 0) {
// 表示有找到分界点,然后就从后往前找到第一个比nums[i]大的做交换
while (nums[j] <= nums[i]) --j;
// 这个while循环在找到第一个nums[j] > nums[i]时退出
// 下面就是做交换
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
// 因为前面已经保证了逆序,这里只要排成正序就是最小的了
Arrays.sort(nums, i+1, nums.length);
}
0%