王某某的笔记

记录我的编程之路

简单记录一下,大概明白什么是IPV6

IPV6出现原因

2019 年 11 月 25 日已分配完公网 IPv4 地址,以后就没有多余地址可以分配了。短期内可以使用 NAT 技术进行缓解。长期来看,还是要用 128 位的 IPv6 地址替代 32 位的 IPv4 地址,IPv6 有 3.4×10^38 个可用地址,多得不得了,可以满足未来 IP 地址的需求。

IPV4表示

32 位的 IPv4 地址,分隔成 4 个 8 位段,每 8 位段的值在 0 ~ 255 之间,每个 8 位段之间用 “ . ” 分开,这就是 “ 点分十进制表示法 ” 。举个栗子:

192.168.0.1

IPv6 地址表示

从 IPv4 到 IPv6 最显著的变化就是网络地址的长度,IPv6 地址为 128 位长度,
是 IPv4 地址的 4 倍,如果用点分十进制表示法,那么会有 16 个八位组,地址过于冗长。
为了使用方便, 使用十六进制表示法,分隔成 8 个 16 位段,每 16 位段的值在 0000 ~ FFFF 的十六进制数之间,每个 16 位段之间用 “ : ” 分开。
共 32 个十六进制数,通常写做8组每组4个十六进制的形式。

例如:

2001:1111:0100:000a:0000:00bc:2500:0a0b
2001:0db8:85a3:08d3:1319:8a2e:0370:7344

简化表达

但是 IPv6 地址还是太长,不方便记忆,看着都头晕,也不方便书写,毫无规律可言。于是就有了两条简化规则。第一条规则是:

1. 每组十六进制数中开头的 0 可以省略
上面的 IPv6 地址可以写成:2001:1111:100:a:0:bc:2500:a0b

开头的 0 才能省略,末尾的 0 是不能省略的

2. 由全 0 组成的连续的 16 位段可以用一对冒号 “ :: ” 表示

如果四个数字都是零,则可以被省略
2001:0db8:85a3:0000:1319:8a2e:0370:7344
等同于
2001:0db8:85a3::1319:8a2e:0370:7344

如果因为省略而出现了两个以上的冒号的话,可以压缩为一个,但这种零压缩在地址中只能出现一次。

  • 2001:0DB8:0000:0000:0000:0000:1428:57ab
  • 2001:0DB8:0000:0000:0000::1428:57ab
  • 2001:0DB8:0:0:0:0:1428:57ab
  • 2001:0DB8:0::0:1428:57ab
  • 2001:0DB8::1428:57ab

网段表示

IPv4 的网段地址可以用子网掩码表示,还可以用斜线法表示。IPv6 只能用斜线法表示网段地址,即在 IPv6 地址后面加上一个斜线 “ / ” ,后面加上一个十进制的数字,来表示前面多少位是网络位。网络位是 64 位的 IPv6 地址表示如下:

3001:2222:333:aa:bc::707:9900/64
对应的网段地址是:
3001:2222:333:aa::/64

全是 0 的 IPv6 地址可以写成一对冒号。当网络位是 0 位时,表示默认地址。
::/0

IPv6 地址类型

参考:https://posts.careerengine.us/p/6191e1c1f1e7b34fdab033c3

IPv6 地址根据使用范围和功能,分为三种类型:

  • 单播( Unicast )
  • 任意播( Anycast )
  • 组播( Multicast )

对比 IPv4 ,IPv6 地址中没有广播地址,但是有一个包含全部节点的组播地址,跟 IPv4 中的广播地址功能相同。
其中单播地址又细分为全球单播地址唯一本地地址链路本地地址等。

全球单播地址

单播地址表示单台设备的地址。全球单播地址是指这个单播地址是全球唯一的。也就是说,全球单播地址是可以在公网使用、全网可路由的 IPv6 地址,类似于 IPv4 的公网 IP 地址。全球单播 IPv6 地址是由 Internet 地址授权委员会( IANA )分配给地区 Internet 注册机构( RIR ),再由 RIR 分配给 Internet 服务提供商( ISP )。
IANA 分配 128 位的 IPv6 地址时,同 IPv4 一样,也是分配一个网段,即网络/子网位,不会分配 128 位的地址。IPv6 单播地址的通用格式如下:

1
【3位】【45位 全球路由前缀】【16位 子网ID】【64位 接口ID】

全球单播 IPv6 地址的前 3 位固定为 001 ;第 4 ~ 48 位的这 45 位由地址分配机构分配;48 位之后的 16 位是网络划分子网位,称为子网 ID ;剩余的 64 位 IPv6 地址就是主机位,但是叫做接口 ID( Interface ID )。因为一台主机可以有几个接口,用 IPv6 地址表示主机的一个接口更准确,而不是表示一台主机。同时,一个接口可以有多个 IPv6 地址,还可以有一个 IPv4 地址,接口 ID 只是这个接口的几个标识符之一。

IANA 和 RIR 把长度 /32 或 /35 的 IPv6 前缀分配给本地 Internet 注册机构( LIR )。LIR 通常是大型的 ISP ,LIR 分配前缀长度 /48 的 IPv6 地址给各个客户。也有一些例外,会分配不同长度的前缀:

  • 如果一个客户非常庞大,那么可以分配一个长度小于 /48 的前缀。
  • 如果有且仅有一个子网需要地址,那么可以分配一个长度是 /64 的前缀。
  • 如果有且仅有一台设备需要地址,那么可以分配一个长度是 /128 的前缀。

唯一本地地址

虽然 IPv6 地址非常充足,但是 IANA 还是分配了一段可以在私有网络使用的私有 IP 地址空间。这种可以自行使用而不用申请的单播 IPv6 地址叫做唯一本地地址。唯一本地地址只能在私有网络使用,不能在全球路由,不同的私网可以复用这类地址。它的作用和范围跟 IPv4 的私有 IP 地址相同。

唯一本地地址的第 8 位比较特殊。第 8 位为 0 时,未定义,也就是说,FC00::/8 这个 IPv6 地址前缀属于保留的地址空间。目前私有网络使用的 IPv6 地址是以 11111101 开头的,即前缀为 FD00::/8 的 IPv6 地址。

链路本地地址

IPv6 的链路本地地址( Link-Local Address ),是 IPv4 地址中没有的类型,是 IPv6 新定义的地址类型。

链路本地地址是只在链路内有效的地址。启动 IPv6 时,网络接口会自动配置这样的一个 IPv6 地址,就可以直接和同一链路上的其它设备通信。因为链路本地地址只在链路本地有效,所以这些数据包不会被发送到其它链路上。
链路本地地址的前 10 位固定是 1111111010 ,之后的 54 位固定为 0 ,最后 64 位是接口 ID 。也就是说,链路本地地址的前缀为 FE80::/10 。

如果链路本地地址的前 64 位都是相同的,那么接口如何使用 64 位的接口 ID 进行标识,才能确保链路本地地址在链路中不会出现 IP 地址冲突呢?答案是接口使用自己的物理 MAC 地址来填充接口 ID 字段。理论上接口的 MAC 地址是唯一的,因此通过 MAC 地址生成的接口 ID 和链路本地地址也是唯一的。

把 MAC 地址转换成接口 ID ,使用 MAC-to-EUI64 转换法。简单的讲,就是使用接口的 48 位 MAC 地址,在 MAC 地址中间,也就是 OUI 后面,插入一个固定的十六进制数 0xFFFE ,并把第 7 位的 U/L (全局/本地)位设置为 1 ,这样就转换为一个 64 位的接口 ID 。

未指定地址

未指定地址是 128 位全为 0 的前缀地址,简写成 ::/128 ,相当于 IPv4 中的 0.0.0.0/32 。这个地址不能分配给接口使用,只有当 IPv6 设备还没获取到地址时,才将未指定地址作为数据包的源 IPv6 地址。

回环地址

回环地址是前 127 位全为 0 ,最后一位是 1 的 128 位前缀地址,简写成 ::1/128 ,相当于 IPv4 中的回环地址 127.0.0.1/8 。回环地址表示节点自己,不能分配给接口使用。只要设备的协议栈状态正常,设备就可以收到发送给回环地址的数据包。

访问IPV6地址

使用URL方式访问IPv6地址时候,要在IPv6地址前后分别加“[”和“]”,这个不仅仅适用于Java,它本身是RFC2732定义的国际标准格式。

比如下面是个包含IPv6的http URL的例子http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html

IPV6地址分配

IPv6 下的私有网络与 ULA

IPv6 时代,ISP 依然会为普通用户提供“动态” IPv6 地址:每次接入网络时,ISP 会重新分配一个 IPv6 地址前缀,这就使得用户侧所有终端的地址发生变化。如果你的终端设备之间有内部互联,这可能会造成服务中断。我们不希望自己运行的网络受制于 ISP !

解决方法自然是:给你的局域网分配一个 IPv6 的内网地址段吧。 当然我不是让你重新使用 NAT。当然 IPv6 依然支持 NAT,如果你愿意,可以为你的终端分配内网 IPv6 地址,然后在网关处部署 NAT66,你就又重新再次回到了 NAT 时代。不过通常而言,NAT66 是没有必要的,而且使用 NAT 技术还会造成非常明显的性能损失。

在 IPv6 网络下,可以为每一个网络接口分配多个 IPv6 地址。你可以为你局域网的设备同时分配公网和内网地址,使你的设备同时接入内外两张网。

IPv6 提供一段称为 Unique Local Address ( ULA )的地址段fc00::/7,包含fc00::/8和fd00::/8两部分:目前fc00::/8的使用还没有定义,我们不去管它;fd00::/8可以被自由使用。fd00::/8是一个十分巨大的地址段,你可以从中挑选一个/48的子段分配给你的局域网。RFC 4193建议使用随机生成的方法,使每个局域网的地址段都不同(这也是 ULA 名字的由来),从而避免局域网合并时地址冲突的麻烦。当然这主要是针对企业而讲的,家庭使用的话随机生成还是挑个好记的自己斟酌。如果要随机生成,Google 一下ULA generator有真相。

有了 ULA,你的设备在内部互访时可以直接使用固定的 ULA,和外部互访时则使用公网地址。只要保证你的路由表正确就可以:默认路由使用公网地址作为出口,ULA 地址段使用 ULA 作为出口。如果出现问题,检查一下你的 Router Advertisement 和 DHCPv6 服务的设置吧。

路由通告服务

在Openwrt中看到的


NDP

IPv6 的邻居发现协议( NDP )相当于 IPv4 的 ARP 、ICMP 的路由器发现和 ICMP 的重定向,还可以发现网络中使用的 IPv6 地址前缀等参数,并实现地址自动配置等。IPv6 协议通过 NDP 功能实现即插即用特性:

  • 路由器发现(Router Discovery):当一个节点接入到 IPv6 链路时,它可以发现链路上的路由器,而不需要借助使用 DHCP 。
  • 前缀发现(Prefix Discovery):当一个节点接入到 IPv6 链路时,它能够发现链路的前缀。
  • 参数发现(Parameter Discovery):节点能够发现所在链路的参数,像链路的 MTU 和跳数限制等。
  • 地址自动配置(Address Autoconfiguration):节点能够自动配置,不需要使用 DHCP 。
  • 地址解析(Address Resolution):节点不需要通过 ARP 就能够获取链路上其它节点的 MAC 地址。
  • 下一跳确定(Next-Hop Determination): 能够确定到达目的节点的下一跳链路层节点,或者所在链路的目的节点,或是到达目的节点的路由器。
  • 邻居不可达检测( Neighbor Unreachability Detection): 节点上能够检测到链路上的邻居何时不可达,邻居有可能是主机,也可能是路由器。
  • 地址冲突检测(Duplicate Address Detection): 节点能够检测到要使用的地址是否已经被其它节点占用。
  • 重定向(Redirect):对于非连接的目的节点,路由器能够通知主机存在更好的下一跳路由。

IPV6 路由

参考资料:https://blog.51cto.com/u_7658423/1339259

ICMPV6代替IPV4中的ARP进行IPv6的MAC地址解析

无论是IPv4还是IPv6的以太网环境,当一台主机需要访问另一台主机时,不只是单纯地依靠IP地址,还必须得依靠MAC地址。比如:主机192.168.1.2通过命令ping去访问主机 192.168.1.1或者一个运行IPv6的主机FEC0::21E:68FF:FEA0:4879通过命令ping另一台IPv6的主机FEC0::0000:4CFF:FE4F:4F50,在这个ping会话的过程中,最关键的是通过MAC地址去实现数据的交互,换而言之,源主机必须获得目标主机的IP地址与MAC地址。无论这些主机是运行IPv4还是IPv6。所以需要一种方式,在知道目标计算机IP地址的情况下去解析目标的MAC地址。并把这个IP地址与MAC对应的关系存储在一张缓存表里面,以便在通信的过程中快速对目标MAC地址进行查找。

IPV4通过ARP广播来获取目标的mac地址

在传统的IPv4网络中,主机通过ARP协议解析目标IP地址与目标MAC地址的对应关系。而ARP协议主要通过广播的方式来实现

IPV6通过ICMPv6实现

在IPv6网络中,对目标MAC地址的发现不再是通过ARP协议来完成。因为IPv6放弃了广播的使用,在IPv6的网络环境中已经不存在广播这个概念了,而ARP地址解析协议是基于广播进行工作的,所以对于IPv6的网络环境而言,ARP协议被永远的放弃。取而代之的是ICMPv6的邻居请求消息(ICMPv6消息类型135)、邻居公告消息(ICMPv6消息类型136)来完成对MAC地址的解析。在这个过程中需要使用一个请求节点组播地址(FF02::1FFxx:xxxx)

6to4

在 IPv6 地址的环境中使用 IPv4 地址,需要用到转换技术,把 IPv4 地址转换成 IPv6 地址。比如 6to4 技术就是将 IPv4 地址转换成 16 进制数,再嵌入到 IPv6 地址的最后 32 位。


验证是否支持IPV6

http://test-ipv6.com/

https://ipv6-test.com/

http://ipv6.baidu.com

访问IPV6地址

直接用IPv6的地址打开,在地址栏输入:
[2400:da00:2::29]

ping 测试

1
ping -6 240e:386:924:f4e1::c47
在线测试

http://www.ipv6now.com.au/pingme.php

curl 测试

1
2
3
curl -g [240e:386:924:f4e1:fe67:3f72:5318:a114]

curl -g -v [240e:386:924:f4e1:fe67:3f72:5318:a114]
  • 使用 -g 参数
  • 使用方括号[] 将ipv6地址括起来
  • 使用 -v 参数 查看具体请求与返回信息

直接访问目标机器的对应接口,不需要在路由器上做端口映射什么的
注意查看防火墙策略,开发80和443端口

在线测试地址

https://8gwifi.org/curlfunctions.jsp

测试速度

http://speed.neu6.edu.cn/

CSV、XML、JSON和YAML都是常用的数据交换格式,它们用于存储和传输数据。

CSV

CSV (Comma-Separated Values): CSV 是一种简单的文件格式,用于存储表格数据,如电子表格或数据库。数据值由逗号分隔,每行代表数据表中的一条记录。

1
2
3
"id","name","age"
"1","John",25
"2","Alice",30

XML

XML (eXtensible Markup Language): XML 是一种用于存储和传输数据的标记语言。它使用标签对数据进行标识,具有良好的可读性和扩展性,非常适合在互联网上传输结构化数据。

1
2
3
4
5
6
<person>
<id>1</id>
<name>John</name>
<age>25</age>
</person>

JSON

JSON (JavaScript Object Notation): JSON 是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。它基于JavaScript编程语言的一个子集,但独立于语言,广泛应用于Web应用程序中数据交换。

1
2
3
4
5
6
{
"id": 1,
"name": "John",
"age": 25
}

YAML

YAML (YAML Ain’t Markup Language): YAML 是一种直观的数据序列化格式,用于配置文件、数据交换等。它比XML或JSON更加简洁,并且是面向对象的,支持复杂的数据结构。

1
2
3
4
id: 1
name: John
age: 25

作用

这些格式通常用于以下场景:

  • 数据交换: 在不同的计算环境、系统或程序之间交换数据。
  • 配置文件: 存储程序设置和配置。
  • 数据存储: 以一种结构化的方式存储信息,便于检索和分析。

外网访问SSH

开22端口不安全,这里在防火墙上做一个转发,端口:61622

vi /etc/config/firewall

1
2
3
4
5
6
7
8
config redirect             
option name 'Allow-Wan-SSH-61622'
option src 'wan'
option src_dport '61622'
option dest 'lan'
option dest_port '22'
option proto 'tcp'
option target 'DNAT'

IP白名单设置

如公司的出口IP为:123.240.202.234

临时的

1
iptables -I INPUT -s 123.240.202.234 -j ACCEPT

重启之后就没有了

永久的

编辑 vi /etc/config/firewall
加上如:

1
2
3
4
5
6
config rule
option name 'Company Ip White List'
option src 'wan'
option src_ip '123.240.202.234'
option family 'ipv4'
option target 'ACCEPT'

重启:/etc/init.d/firewall restart

直接开放wan上的22口

修改防火墙文件

1
vi /etc/config/firewall 

在末尾加上

1
2
3
4
5
6
config rule
option name 'Allow-Wan-SSH-22'
option src 'wan'
option dest_port '22'
option target 'ACCEPT'
option proto 'tcp'

重启防火墙,并测试

1
2
3
service firewall restart
# 或
/etc/init.d/firewall restart

密码要搞复杂点



其他防火墙配置的例子

https://openwrt.org/zh-cn/doc/uci/firewall

开放端口

1
2
3
4
5
6
config rule
option src wan
option dest_port 22
option target ACCEPT
option proto tcp

使Internet上的计算机可以使用SSH访问路由器

端口转发(NAT/DNAT)

1
2
3
4
5
config redirect
option src wan
option src_dport 80
option proto tcp
option dest_ip 192.168.1.10

源NAT (SNAT)

1
2
3
4
5
6
7
config redirect
option src lan
option dest wan
option src_ip 10.55.34.85
option src_dip 63.240.161.99
option dest_port 123
option target SNAT

源NAT更改发往系统的传出数据包,以使系统看起来像是该数据包的源。
为定向到端口123的,从IP地址为10.55.34.85的主机发起的UDP和TCP流量定义源NAT。源地址被重写为63.240.161.99。

当单独使用时,源NAT用来限制一台计算机访问互联网,但允许它访问一些服务,我手动转发一些似乎是本地服务,如NTP到互联网。

实际端口转发

1
2
3
4
5
6
config redirect
option src wan
option src_dport 80
option dest lan
option dest_port 80
option proto tcp

限制指定机器

1
2
3
4
5
config rule
option src lan
option dest wan
option dest_ip 123.45.67.89
option target REJECT

阻止所有到指定主机地址的连接尝试。

通过MAC限制访问互联网

1
2
3
4
5
config rule
option src lan
option dest wan
option src_mac 00:00:00:00:00
option target REJECT

则阻止从客户端到Internet的所有连接尝试。

转发规则限制

1
2
3
4
5
6
config rule
option src lan
option dest wan
option dest_port 1000-1100
option proto 'tcp udp'
option target REJECT

创建一个转发规则,以拒绝端口1000-1100上从lan到wan的流量。

透明代理规则(同一主机)

1
2
3
4
5
config redirect
option src lan
option proto tcp
option src_dport 80
option dest_port 3128

通过侦听路由器本身端口3128上的代理服务器将所有来自lan的HTTP通信重定向到lan。

透明代理规则(外部)

1
2
3
4
5
6
7
config redirect
option src lan
option proto tcp
option src_ip !192.168.1.100
option src_dport 80
option dest_ip 192.168.1.100
option dest_port 3128

侦听端口3128的192.168.1.100处的外部代理将来自lan的所有传出HTTP流量重定向。

简单DMZ规则

1
2
3
4
config redirect
option src wan
option proto all
option dest_ip 192.168.1.2

所有协议的所有WAN端口重定向到内部主机192.168.1.2。

Spring 表达式语言 (SpEL)

使用 Spring 的表达式接口进行表达式求值

下面的代码引入了 SpEL API 来评估文字字符串表达式 ‘Hello World’

1
2
3
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'");
String message = (String) exp.getValue();

常用的 SpEL 类和接口位于包org.springframework.expression 及其子包和spel.support中。

作为方法调用的示例,我们在字符串文字上调用“concat”方法

1
2
3
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')");
String message = (String) exp.getValue();

这将返回 ‘Hello World!’

使用点号来获取属性

1
2
3
4
ExpressionParser parser = new SpelExpressionParser();
// invokes 'getBytes().length'
Expression exp = parser.parseExpression("'Hello World'.bytes.length");
int length = (Integer) exp.getValue();

调用方法

1
2
3
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()");
String message = exp.getValue(String.class);

EvaluationContext接口

EvaluationContext在评估表达式以解析属性、方法、字段并帮助执行类型转换时使用该接口。开箱即用的实现使用反射来StandardEvaluationContext操作对象,缓存 j ava.lang.reflect的Method和 实例以提高性能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Simple {
public List<Boolean> booleanList = new ArrayList<Boolean>();
}

Simple simple = new Simple();

simple.booleanList.add(true);

StandardEvaluationContext simpleContext = new StandardEvaluationContext(simple);

// false is passed in here as a string. SpEL and the conversion service will
// correctly recognize that it needs to be a Boolean and convert it
parser.parseExpression("booleanList[0]").setValue(simpleContext, "false");

// b will be false
Boolean b = simple.booleanList.get(0);

语法参考

https://docs.spring.io/spring-framework/docs/3.0.0.M4/reference/html/ch06s05.html

文字表达式

支持的文字表达式类型有字符串、日期、数值(整数、实数和十六进制)、布尔值和空值。字符串由单引号分隔。

1
2
3
4
5
6
7
8
9
10
11
12
13
ExpressionParser parser = new SpelExpressionParser();

// evals to "Hello World"
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();

double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();

// evals to 2147483647
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();

boolean trueValue = (Boolean) parser.parseExpression("true").getValue();

Object nullValue = parser.parseExpression("null").getValue();

数字支持使用负号、指数记数法和小数点。默认情况下,使用 Double.parseDouble() 解析实数。

属性、数组、Lists、Maps、Indexers

使用来表示属性引用

1
2
3
int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context); 

String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);

数组和列表的内容是使用方括号表示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ExpressionParser parser = new SpelExpressionParser();

// Inventions Array
StandardEvaluationContext teslaContext = new StandardEvaluationContext(tesla);

// evaluates to "Induction motor"
String invention = parser.parseExpression("inventions[3]").getValue(teslaContext,
String.class);

// Members List
StandardEvaluationContext societyContext = new StandardEvaluationContext(ieee);

// evaluates to "Nikola Tesla"
String name = parser.parseExpression("Members[0].Name").getValue(societyContext, String.class);

// List and Array navigation
// evaluates to "Wireless communication"
String invention = parser.parseExpression("Members[0].Inventions[6]").getValue(societyContext,
String.class);

map的内容是通过在括号内指定文字键值来获得的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Officer's Dictionary

Inventor pupin = parser.parseExpression("Officers['president']").getValue(societyContext,
Inventor.class);

// evaluates to "Idvor"
String city =
parser.parseExpression("Officers['president'].PlaceOfBirth.City").getValue(societyContext,
String.class);

// setting values
parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(societyContext,
"Croatia");

方法

使用典型的 Java 编程语法调用方法。还可以在文字上调用方法,支持方法参数

1
2
3
4
5
6
// string literal, evaluates to "bc"
String c = parser.parseExpression("'abc'.substring(2, 3)").getValue(String.class);

// evaluates to true
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(societyContext,
Boolean.class);

运算符号

关系运算符

使用标准运算符表示法支持等于、不等于、小于、小于或等于、大于和大于或等于。

1
2
3
4
5
6
7
8
// 计算结果为真
boolean trueValue = parser.parseExpression( " 2 == 2" ).getValue(Boolean.class );

// 计算结果为假
boolean falseValue = parser.parseExpression( " 2 < -5.0" ).getValue(Boolean.class );

// 计算结果为真
boolean trueValue = parser.parseExpression( " 'black' < 'block'" ).getValue(Boolean.class );

除了标准的关系运算符之外,SpEL 还支持“instanceof”和基于正则表达式的“匹配”运算符。

1
2
3
4
5
6
7
8
9
10
// evaluates to false
boolean falseValue = parser.parseExpression("'xyz' instanceof T(int)").getValue(Boolean.class);

// evaluates to true
boolean trueValue =
parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);

//evaluates to false
boolean falseValue =
parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);

逻辑运算符

支持的逻辑运算符有 and、or 和 not。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// -- AND --

// evaluates to false
boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);

// evaluates to true
String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

// -- OR --

// evaluates to true
boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class);

// evaluates to true
String expression = "isMember('Nikola Tesla') or isMember('Albert Einstien')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

// -- NOT --

// evaluates to false
boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);


// -- AND and NOT --
String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

数学运算符

加法运算符可用于数字、字符串和日期。减法可用于数字和日期。乘法和除法只能用于数字。其他支持的数学运算符是模数 (%) 和指数幂 (^)。执行标准运算符优先级。这些运算符如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Addition
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2

String testString =
parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class); // 'test string'

// Subtraction
int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4

double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000

// Multiplication
int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6

double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0

// Division
int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2

double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0

// Modulus
int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3

int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1

// Operator precedence
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21

赋值

类型

构造器

变量

可以使用语法#variableName 在表达式中引用变量。使用 StandardEvaluationContext 上的 setVariable 方法设置变量。

1
2
3
4
5
6
7
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
StandardEvaluationContext context = new StandardEvaluationContext(tesla);
context.setVariable("newName", "Mike Tesla");

parser.parseExpression("Name = #newName").getValue(context);

System.out.println(tesla.getName()) // "Mike Tesla"

#this 变量

变量 #this 始终被定义并引用当前评估对象

函数

三元运算符(If-Then-Else)

https://github.com/spring-cloud/spring-cloud-commons/blob/main/docs/src/main/asciidoc/spring-cloud-commons.adoc

注册是注册了一个不能访问的地址

默认是 eth0,比如我们要忽略这个网卡

直接再nacos上增加配置

1
2
3
4
spring:
cloud:
inetutils:
ignored-interfaces: eth0

优先获取ip

1
spring.cloud.inetutils.preferred-networks=10.34.12

使服务优先获取前缀为10.34.12的IP

如果选择固定网卡配置项

1
2
spring.cloud.nacos.discovery.networkInterface = eth0

固定IP地址

1
2
3
4
5
6
spring:
cloud:
nacos:
discovery:
ip: 10.13.19.91

洗白的方式比较麻烦

安装第三方FFmpeg是最快的解决方法

添加第三方源

设置第三方源

套件中心 —> 设置 —> 常规 —> 选择任何发行者

套件中心 —> 设置 —> 套件来源 —> 新增

如果添加时提示:无效的位置
查了下是系统证书过期导致的,更新证书

1
2
sudo mv /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt.bak && sudo curl -Lko /etc/ssl/certs/ca-certificates.crt https://curl.se/ca/cacert.pem

几个常用的第三方套件源:

安装第三方ffmpeg

套件中心 —> 社群 —> 安装ffmpeg

备份原始ffmpeg、创建第三方ffmpeg软连接

用ssh工具连接到群晖,切换到root账号

1
sudo -i

备份

1
2
mv /usr/bin/ffmpeg /usr/bin/ffmpeg_back

创建第三方ffmpeg软连接

1
ln -s /volume1/@appstore/ffmpeg/bin/ffmpeg /usr/bin/ffmpeg

重建Moments索引

进入Moments套件,设置——常规——重建索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class TestApplicationRunner implements ApplicationRunner {

@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;

@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("启动后运行");
System.out.println(nacosDiscoveryProperties.getIp());
System.out.println(nacosDiscoveryProperties.getPort());
}

}

官方地址:
https://kb.synology.com/en-global/DSM/tutorial/What_network_ports_are_used_by_Synology_services

Web Applications

Type Port Number Protocol
DSM 5000 (HTTP), 5001 (HTTPS) TCP
File Station 5000 (HTTP), 5001 (HTTPS) TCP

File Transferring

Type Port Number Protocol
AFP 548 TCP
CIFS smbd: 139 (netbios-ssn), 445 (microsoft-ds) TCP/UDP
CIFS Nmbd: 137, 138 UDP
FTP, FTP over SSL, FTP over TLS 21 (command),20 (data connection in Active Mode), 1025-65535 (data connection in Passive Mode) TCP
iSCSI 3260, 3263, 3265 TCP
NFS 111, 892, 2049 TCP/UDP
TFTP 69 UDP
WebDAV, CalDAV 5005, 5006 (HTTPS) TCP

Mobile Applications

Type Port Number Protocol
DS audio 5000, 5001 (HTTPS) TCP
DS cam 5000, 5001 (HTTPS) TCP
DS cloud 6690 TCP
DS file 5000 (HTTP), 5001 (HTTPS) TCP
DS finder 5000 (HTTP), 5001 (HTTPS) TCP
DS get 5000 (HTTP), 5001 (HTTPS) TCP
DS note 5000 (HTTP), 5001 (HTTPS) TCP
DS photo 80, 443 (HTTPS) TCP
DS video 5000 (HTTP), 5001 (HTTPS) TCP
Synology Drive 5000 (HTTP), 5001 (HTTPS) TCP
Synology Moments 5000 (HTTP), 5001 (HTTPS) TCP
Synology Photos 5000 (HTTP), 5001 (HTTPS) TCP

群晖套件安装设置

从套件中心安装媒体服务器

打开媒体服务器进行设置

常规设置
  • DMA选单语言:中文
  • SSDP 广告间隔(秒):92 (之后再改成920)
浏览设置
  • 整合 Video Station 数据库至媒体服务器
    (这样共享的目录中会多一个Video Station文件夹,默认是:视频、音乐、照片)
DMA 兼容性

注意:不要勾选“限制新检测到的UPnP/DLNA设备对媒体服务器的访问”

单击设备列表,查看设备,如果不能访问,尝试改变配置文件类型

索引文件夹配置

若要添加更多索引文件夹,请进入:控制面板>索引服务>媒体索引>索引文件夹>创建。
文件类型让您决定要包含在索引文件夹中的媒体文件类型。

客户端使用

小米电视

直接打开 高清播放器

win10

  1. 我的电脑 -> 访问媒体 -> 连接到媒体服务器
  2. 打开Windows Media Player -> 流媒体菜单启用流媒体 -> 左侧其他媒体库中就可以看到了

已有公网IP
已配置DDNS,IP地址绑定了域名 (阿里云域名)
已经设置了ssl证书 (阿里云免费)

WebDAV

基于Web的分布式编写和版本控制(WebDAV)是超文本传输协议(HTTP)的扩展,有利于用户间协同编辑和管理存储在万维网服务器文档。

WebDAV协议为用户在服务器上创建、更改和移动文档提供了一个框架。WebDAV协议最重要的功能包括维护作者或修改日期的属性、名字空间管理、集合和覆盖保护。维护属性包括创建、删除和查询文件信息等。名字空间管理处理在服务器名称空间内复制和移动网页的能力。集合(Collections)处理各种资源的创建、删除和列举。覆盖保护处理与锁定文件相关的方面。

许多现代操作系统为WebDAV提供了内置的客户端支持。

一、配置WebDAV

群晖按照 WebDAV套件,设置启用HTTPS,端口为5006

二、路由器配置端口映射

如将路由器端口 15006 映射到 192.168.1.6(群晖)的 5006端口上

三、使用RaiDrive软件来映射WebDAV网络磁盘

安装 RaiDrive 【RaiDrive_1.5.3.1.exe】
需要安装 .net FramWork

安装完成之后点击 添加 WebDAV

地址: https://xxxxxx.com 端口:15006
path:/

Account填写你的账号和密码




其他问题

问题1:登录时提示没有权限

  • 检查用户密码是否错误,用admin账号试试看
  • 检查群晖的控制面板 -> 权限 -> WebDAV Server 【使用需要登录的账号 允许访问】
0%