跳到主要内容

· 阅读需 2 分钟

介绍三种不用中间变量实现数值交换的方法,

1. 加减法

假设有变量

let a = 2, b = 5

交换

a = a + b // a = 7

b = a - b // b = 2

a = a - b // a = 5

缺点:

  • 只能处理数值
  • 数值太大相加的时候可能会溢出

2. 乘除法

有加减法那么就自然可以联想得到乘除法:

a = a * b // a = 10

b = a / b // b = 2

a = a / b // a = 5

缺点:

  • 会有精度损失
  • 被除数不能为 0

3. 异或法

异或是一种数学运算,不同为 1,相同为 0:

结果
000
011
101
110

在计算机中进行异或运算,需要先把数值换算成二进制,a 的二进制为 010b 的二进制为 101

a = a ^ b // 010 ^ 101 = 111

b = a ^ b // 111 ^ 101 = 010

a = a ^ b // 111 ^ 010 = 101

缺点:

  • 无法处理浮点型变量

总结

三种方法都是奇技淫巧,仅供学习使用,生产环境最好不要用。

· 阅读需 5 分钟

什么是 IP 欺骗?

伪装源地址的 IP 欺骗包(Spoofed IP packets)通常用于攻击中,它的目的是在攻击中避免源地址被侦测得到。

IP 欺骗是创建具有修改源地址的 Internet 协议 (IP) 数据包,以便隐藏发送者的身份、冒充另一个计算机系统或两者兼而有之。 这是一种经常被坏人用来对目标设备或周围基础设施发起 DDoS 攻击的技术。

发送和接收 IP 数据包是联网计算机和其他设备进行通信的主要方式,是现代互联网的基础。 所有 IP 数据包都包含一个位于数据包主体之前的标头,并包含重要的路由信息,包括源地址。 在普通数据包中,源IP地址是数据包发送者的地址。 如果数据包被欺骗,源地址将会被伪造。

ip-spoofing

IP 欺骗类似于攻击者发送一个数据包给某个人,这个人会拿到一个错误的返回地址。如果收到数据包的这个人想要停止发件人发送数据包,通过阻止虚假地址的数据包的手段是起不到作用的,因为地址相当容易更改。相关地,如果接收方想要响应返回地址,他们的响应包会去到别的地方,而不是真正的发送者。 伪装数据包地址的能力是许多 DDoS 攻击利用的核心漏洞。

DDoS 攻击通常会利用欺骗,目的是用流量淹没目标地址设备,同时掩盖恶意源地址的身份,阻止缓解措施。 如果源 IP 地址被伪造并不断随机化,阻止恶意请求变得相当困难。 IP 欺骗还使执法和网络安全团队难以追踪攻击的肇事者。

欺骗还用于伪装成另一个设备,以便将响应发送到该目标设备。 NTP 放大和 DNS 放大等容量攻击就是利用这个漏洞。 修改源 IP 的能力是 TCP/IP 设计所固有的,使其成为一个持续的安全问题。

与 DDoS 攻击相关联的是,欺骗攻击还可以伪装成另一个设备,以避开身份验证,获得访问或“劫持”用户会话的权限。

如何防止 IP 欺骗(包过滤)

虽然无法防止 IP 欺骗,但可以采取措施阻止欺骗数据包渗入网络。 一种非常常见的欺骗防御措施是入口过滤,在 BCP38(最佳通用实践文档)中进行了概述。 入口过滤是一种数据包过滤形式,通常在网络边缘(network edge)设备上实现,它检查传入的 IP 数据包并查看它们的源头。 如果这些数据包上的源头与它们的来源不匹配,或者它们看起来很可疑,则数据包将被拒绝。 一些网络还将实施出口过滤,它查看离开网络的 IP 数据包,确保这些数据包具有合法的源头,以防止网络中的某人使用 IP 欺骗发起出站恶意攻击。

Reference

[1] What is IP spoofing? https://www.cloudflare.com/learning/ddos/glossary/ip-spoofing/

· 阅读需 6 分钟

在桌面端应用上,Electron 也早已做大做强,GitHub桌面端、VSCode、Figma、Notion、飞书、剪映、得物都基于此。但最近后起之秀的 Tauri 也引人注目,它解决了 Electron 一个大的痛点——打包产物特别大

我们知道 Electron 基于谷歌内核 Chromium 构建,打包后无论应用多小,至少都得 70M 起步,而 Tauri 使用操作系统内的 Webview1,运行时才会去动态连接 webview,这使得它的打包速度非常快、打包后的应用更小

Tauri 跟 Electron 一点不同,Electron 使用 JavaScript 编写后台服务,而 Tauri 则使用 Rust,Rust 这两年势头很猛,更安全、性能更好,很多应用都开始转入 Rust 的怀抱,相信不久后也会是前端必学基础。

本文就基于 Tauri 作为构建桌面端应用框架,仅需一点时间,就可以将一个 Web 应用转为桌面端应用

1. 打开一个 Web 应用

我们以 FocusTide 这个应用为转换对象,它是 GitHub 开源的一个计时 Web 应用:

首先我们先 Clone 该仓库到本地:

$ git clone git@github.com:Hanziness/FocusTide.git

然后我们安装并且运行起来:

# 安装依赖
$ yarn install

# 启动服务,在 localhost:3000
$ yarn dev

2. 安装 Tauri 依赖

我们以 Mac 为例,Mac 下需要安装 CLang 和 MacOS 相关开发依赖:

$ xcode-select --install

安装 Tauri:

$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh

这条命令会安装下载 Rustup,它会下载安装 Rust 相关依赖,当安装成功后控制台会显示:

$ Rust is installed now. Great!

其他操作系统安装 Tauri:

  1. Windows 下安装 Tauri:https://tauri.app/zh-cn/v1/guides/getting-started/prerequisites/#windows
  2. Linux 下安装 Tauri:https://tauri.app/zh-cn/v1/guides/getting-started/prerequisites/#linux

3. 将 Tauri 集合到项目中

在 Web 应用内,先安装 Tauri 相关的 npm 包

$ npm install --save-dev @tauri-apps/cli

pakcage.json 增加脚本命令:

"scripts": {
"tauri": "tauri"
}

接着跑 Tauri 初始化项目命令:

$ npm run tauri init

这条命令执行之后,会在当前 Web 项目产生如下 Tauri 项目:

执行之后,会有一些问题需要我们去填写

  1. What is your app name? 应用名。这个名字会作为打包后应用的正式名称。

  2. What should the window title be? 默认窗口名称。这个会作为我们打开应用窗口的名称,后续我们不需要这个窗口的话我们可以在 tauri.conf.jsonhiddenTitle 字段去隐藏它。

  3. Where are your web assets (HTML/CSS/JS) located relative to the < current dir $gt; /src-tauri/tauri.conf.json file that will be created? 生产环境下的文件路径。也就是前端项目打包之后的项目路径,这个路径相对于 /src-tauri/tauri.conf.json 路径。FocusTide 项目打包产物放在项目的 dist 文件夹中,所以我们填 ..dist

  4. What is the URL of your dev server? 开发环境下的服务路径。FocusTide 项目开发下的服务路径为 http://localhost:3000

  5. What is your frontend dev command? 前端启动开发命令。FocusTide 项目启动开发命令是 yarn dev

  6. What is your frontend build command? 前端打包命令。FocusTide 项目的打包命令是 yarn generate

执行后,生成 src-tauri,接着我们就可以把项目跑起来了:

$ npm run tauri dev

可以看到,我们的应用在窗口跑起来了:

4. 打包发布

如果开发 OK,我们就可以把应用打包出来。

$ npm run tauri build

执行打包命令后,打包应用会存放在 src-tauri/target/release/bundle 下,可以看到,打包产物非常小:

Tauri 打包过程中,会更根据当前系统平台打包,比如 Mac 下只能打包 .dmg.app 包,Windows 下打包 .msi.exe 包。

5. 部署

下载后,如果我们要公开这个应用,需要部署应用,这里我们推荐 Laf ,我们可以使用它的云存储#文件管理,把我们的应用上传上去并且得到下载链接:

6. 最后

整个转换过程其实非常简单快速,如果你花费了超过 10 分钟的话,那我们深表歉意。如果你要继续深入使用桌面后台服务,可以查看 Tauri 官网

最后,我把转换后的 FocusTide 项目放到了个人 GitHub 上,并且取名为「来做」,目前仅限 Mac 端,欢迎下载 👏🏻

Footnotes

  1. Tauri vs. Electron: A comparison, how-to, and migration guide: https://blog.logrocket.com/tauri-electron-comparison-migration-guide/

· 阅读需 1 分钟

1bit 是计算机中最小的数据单位,1bit 就对应一个高低电位。

1bit ×\times 8 == 1byte

1byte ×\times 1024 == 1KB (kilobyte)

1KB ×\times 1024 == 1MB (megabyte)

1MB ×\times 1024 == 1GB (gigabyte)

1G ×\times 1024 == 1TB (terabyte)

1TB ×\times 1024 == 1PB (petabyte)


在 UTF-8 编码中

  1. 一个 US-ASCII 字符只需要 1byte
  2. 带有变音符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文等字符需要 2byte
  3. 其他语言(包括中日韩文字、东南亚文字、中东文字等)使用 3byte
  4. 极少数语言用 4byte

· 阅读需 1 分钟

SemVer 是目前较为流行的命名版本的规范,它的全称是 Semantic Versioning Specification,语义化版本命名规范。

它对版本的命名构成主要为:

  • X,Major 版本,版本迭代,会有兼容问题
  • Y,Minor 版本,更新功能,不应该有兼容问题
  • Z,Patch 版本,修复 Bug,更小程度的变动,不会有兼容问题
  • Pre-release,预发布版本,主要是 alpha、beta、insiders,正式发布前的不稳定版本,用于内部、社区、提前体验的用户
  • Build,一般是打包构建时的 commit id

SemVer 的规则简单明了,学习成本极低,这大概就是为什么 SemVer 如此流行的原因了。

[1] https://semver.org/spec/v2.0.0.html [2] https://www.baeldung.com/cs/semantic-versioning

· 阅读需 3 分钟

刚开始学并发和并行时总是整得有点懵,现在终于下手来好好梳理着两者的关系了。

首先并发的单词是 Concurrency,词根 con - 的意思是“一起”,而 current - 词根的意思是“当前”,所以总的意思就是“一起发生的事情”

并行的单词是 Parallelism,词根 parallel - 是平行、并列的意思,所以总的意思就是“平行发生的事情”,也可以说“同时发生的事情”

举一个知乎答主提到的一个形象的例子:

你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。

你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。

你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。

并发的关键是你有处理多个任务的能力,不一定要同时。并行的关键是你有同时处理多个任务的能力。所以我认为它们最关键的点就是:是否是『** 同时 **』。

从这位知乎答主的我们其实就能够知道:并行是并发的一种特殊情况。 并发只要求有处理多个任务的能力,而并行要求有 ** 同时 ** 处理多个任务的能力

借此,我们来梳理一下多核 CPU 和单核 CPU 的并发、并行性

首先是针对单核 CPU,单核 CPU 很明确,一个时刻只能做一件事情,所以它不可能具备并行性,但它可能具备并发性(如果这台 CPU 支持处理多个任务的能力的话)

其次是多核 CPU,多核 CPU 就是多个单核 CPU 组成的,那么同一时刻处理多个任务就是一件轻而易举的事情了,也就是具有了并行性(有并行性就一定有并发性,前面我们已经说了并行性是并发性的一种特殊情况)

· 阅读需 1 分钟

之前一直对对数 loglog 一直不太了解,最近看到算法图解中给对数的另一个解释,挺有意思的,在此记录一下,比如有一个对数:

log28log_28

它可以看做多少个 2 相乘等于 8,答案当然是 3,毕竟 23=82^3=8

2×2×2=82\times2\times2=8

上面的角度是以底数为主的,再推而广之一下,若以真数为主,也可以看作是 8 连续除以多少个 2 等于 1

8÷2÷2÷2=18\div2\div2\div2=1

· 阅读需 5 分钟

1. 什么是进制

  • 进制 进制全称“进位计数制”,一种计数方式,比较常用的是十进制
    • 十进制 十进制就是逢十进一,所以十进制有 10 个数字表示——0,1,2,3,4,5,6,7,8

生活中除了十进制还有很多常见的进制,可能你没有注意到,比如时钟,秒数是 60 进制,秒针从 0 走到 59 后,再走一分钟逢 60 进一成为一分钟。

再说计算机,计算机中也可以采用十进制来代表数字,那为什么偏偏要用二进制呢?原因很简单,因为二进制只有 0 和 1,表示起来很简单。

那为什么说二进制为什么简单呢?因为计算机底层也是硬件,每次硬件在两条路径和十条路径中选择肯定前者来得轻松,只不过为了达到同样目的地,二进制需要多走几步。

2.话说二进制

我们先来看看十进制是怎么表示的,假设我们现在有一个十进制的数字 (3107)10(3107)_{10}

注意

  1. 这里数字 (3107)10(3107)_{10} 用括号括起来,下标是10,表示它是10进制 这个下标就是用来表明它是什么进制的,比如 (3107)8(3107)_{8} ,==注意此时它的下标是 8,那它就不是十进制了==,而是八进制了 另外,为什么平时不这些写呢?因为十进制太通用了,不写默认它就是十进制
  2. 除了这种表示方法,常用的还有后缀字母表示法,你可能遇到过: 比如 3107H,在==数字后面加上字母H,表明它是个十六进制的数字==

这个 (3107)10(3107)_{10} 我们拆开来看的话:

3×103+1×102+0×101+7×1003×10^3+1×10^2+0×10^1+7×10^0

我们再来看一个二进制数字(1101)2(1101)_2,它也可以表示为

1×23+1×22+0×21+1×201×2^3+1×2^2+0×2^1+1×2^0

看到这里你应该能明白到为什么叫做十进制和二进制的原因了吗?

再说进制的转化,二进制转十进制很简单,你把

1×23+1×22+0×21+1×201×2^3+1×2^2+0×2^1+1×2^0

接下来按十进制来计算

8+4+0+18+4+0+1

就等于 (13)10(13)_{10},直接写成 (1101)2(1101)_2=(13)10(13)_{10}

3.什么是ASCII码

  • ASCII ((American Standard Code for Information Interchange)美国信息交换标准代码),名字怪长的,我们只着眼于代码这两个字就好了

我们知道,计算机表示数字是比较简单的,但如何表示文本呢?比如 a、b、c、d 这些英文,甚至是标点符号、汉字等等

前人做了一个很好的替换,就是先建一个表,表中存放着这些文本符号

文本符号
a
b
c

再给这些文本编个顺序

号码文本符号
0a
1b
2c

你要取的时候,按照号码来就好了,想要 a 输入 1,想要 b 输入 2,依次类推(这时你可能会明白一点“代码”的意思)

那么 ASCII 码表是作为较早期的符号表,多早呢?1967年,当时美国人发明的,要知道美国人用的语言就 26 个字母,再加上一些乱七八糟的符号等等,用 128 个字符就能够涵盖了

那为什么是 128 这个数字呢?在前面提到,计算机是用二进制表示的,从 0 号开始编码,一直到 127 号,刚好就是 128 个号码

那么最小号码是 0 号,最大号码是 127 号,那么如果我们能用数字表示最大的号码,那么比他小的号码也都能表示,而 (1111111)2(1111111)_2 就等于 127,我们展开来看

1×26+1×25+1×24+1×23+1×22+1×21+1×201×2^6+1×2^5+1×2^4+1×2^3+1×2^2+1×2^1+1×2^0

另外,这个(1111111)2(1111111)_2 一共有7位,所以也可以说成用 7 位表示的ASCII码表