RegameDesk 开发笔记【3】Boost 1.88.0

前言

今天是 2025-02-20,Boost 1.88.0 还没发布,但目前的版本在实现 RegameDesk 时遇到一些问题,导致使用了不优雅的解决方案,按照稣和作者们的沟通,下个版本都能解决,所以稣认为 1.88.0 才是适合远程桌面​的 Boost 版本。

Why Boost?

在开发过程中,选择一种基础库或库的集合(注:实际上 Boost 是一个集合)几乎是必然的。如果您的产品使用了 Qt,那么许多基础功能很可能会直接借助 Qt 来实现。有些项目可能会选择 Google 的代码作为基础,从而引入 Abseil。甚至还有一些团队会单独提取 Chromium 的 base 模块来使用。

稣曾遇到过有人推荐使用 Folly,而 Folly 本身也依赖于 Boost。如果您能够接受 Folly,那么当团队不再使用它时,接受 Boost 也应该不是问题。

以上例子其实暗示了一个事实:C++ 标准库的功能相对有限,难以满足实际项目的需求。为了解决这一问题,开发团队通常会选择引入第三方库。在选择第三方库时,主要有两种思路:一种是引入一个功能强大且尽可能全面的大型库(集合),再配合少数其它必要的小型库,以最大化地保持代码风格的一致性;另一种则是引入多个专门解决特定需求的小型库,这种方式虽然可以让每个库都“小而美”,但可能会导致代码风格的不一致。选择哪种方式其实很简单——选择您最熟悉的那一种。

注意!上一段说的“小而美”,从整体上看,可能是假象,尤其当项目很大、成员较多时。当然您可以提出 Chromium 来反驳,不过您需要一定实力和精力去驾驭,所以稣只是说“可能”!

对稣来说,Boost 是一个显而易见的选择。它经过多年的发展,积累了丰富的文档和社区讨论,学习和使用难度都不大。过去有人抱怨 Boost 的编译时间过长,现在早已不存在。而且在如今普遍配备 64GB 内存、高速 SSD 的开发环境中,加载大量头文件已经不再是问题,怪罪 Boost 使工程加载变慢的人也能放心了。

片面地安利 Boost

使用 Boost 相比自己实现而言,有以下好处​:

  1. ​开发更快;

  2. 运行更快;

  3. 运行更稳​;

  4. 代码可读性更高。

后面会举真实例子说明为啥是和自己实现比!先说事实,以上三条总有 1~2 条符合,甚至对于某些团队——可能是中 3~4 条。

首先,开发更快,可能是最有争议的,很多人会反驳说——光学它就要很多时间​。这要是放在以前,稣可能想不出啥好招给这部分人洗脑,现在有大语言模型,各种辅助手段,如果还这么说,完全也不用去反驳,没必要了。

另外,Boost 有部分库确实性能不行,并且官方文档也是明说的,这种情况是求稳定,比如 Boost.Format​。

剩下的,Boost 通常有十分优秀的性能,除非极端的具体领域优化,不然大多数人能把性能写赢 Boost 的概率几乎是 1%(多给 1 分,怕您是真大佬!)。举个例子,Boost.JSON 的性能是高于 RapidJSON 的。RapidJSON 这名字起得好(快),不一定就是真的好(快)。

等等,第 4 条是怎么回事?有些人会说他自己手撸的更好理解,如何反驳?嗯,很可能只是对于作者本人才更好理解,别人看都不想看(笑)。作为团队合作的产物,更多共同点才是好的。比如说,整个团队都熟悉 STL,那么基于 STL 的接口/实现就不会差,而把它等价地改为基于某个第三方库,如果没有强力的理由,通常会被(不用脑地)认为不好。自己写的,对别人来说,何尝不是一种“第三方”?显然,大家普遍认可的“第三方”才可能是更好沟通的,更好达成共识的。(注:这里的“大家”是普遍意义上的大家,不是说某个团队里的少数几名成员,毕竟有的团队就两名写代码的,并不存在“多数”和“少数”!)

例子

大家最喜欢的案例分析来了……稣正好遇到这样一个活生生的例子:实现一个 IPC​ 用于 Service 和工作进程之间通信。

这个故事发生在雪蛤油打工时。一开始,稣就打算使用 Boost 封装的 Pipe,因为以前干过类似的活,​有成功案例。但不幸的是,稣是第二个加入团队的,原来已经有人弄过一个实现,纯手撸的,基于共享内存和事件通知​。关键是,大佬说这实现在他前公司用了 2 年很稳定​。稣想了一下,虽然自己的实现代码量不到它的 1/5,但只接受了几周的考验​。还是别冒险,于是那份手撸版本上​了生产。

然后有一个周末,没回家,随手拿 example 改改对比性能,使用 Boost 的版本速度居然是那手撸实现的 4 倍!在应用层,共享内存是 Windows 上最快的 IPC 机制没错,但它需要其它内核对象的辅助,最终完成时速度就拖慢了。Boost 使用 Pipe 作为 IPC 机制,而没用共享内存和事件通知复合实现,很可能作者是知道这门道的​。

对了,每次发送多少字节对性能是有影响的,如果您打算测试,需要对不同大小的信息进行测试,不能用固定的大小,以免得出不全面的​结论。

总结

本文,乃至本系列文章,重点在于心法,并没打算详细介绍项目里用了 Boost 具体哪些类库。因为很简单的道理:您要的功能,如果 Boost 有,考虑用它即可。

看到这里,如果您还记得稣写的基于 Boost 的 IPC 代码,它又多接受了三个月的考验——极其稳定,重点是它用起来简单多了。

声音共享 SoundShare

故事 The Story

SoundShare 是 RegameDesk 开发过程中的副产品。

SoundShare is a by-product of the development process of RegameDesk.

在金山云开发鎏光时,领导给了两个设定:仅用于边缘网络、示范性实现。所以没有使用当时很火的 WebRTC,直接使用 WebSocket 传输音视频裸流。可想而知,在很多场景下,其音频的效果是不好的。

During the development of Liuguang at Kingsoft Cloud, the leadership provided two directives: it should be designed only for edge networks and serve as a demonstrative implementation. Consequently, instead of utilizing the then-popular WebRTC, I directly employed WebSocket to transmit raw audio and video streams. As one might expect, the audio quality was suboptimal in many scenarios.

后来,在雪蛤油开发远程桌面产品时,稣便发现使用 RTC SDK 能有效地改善音频的效果。但是这个 RTC SDK 是公司其它团队开发的,而且开发者人数众多,想必是个大型库。稣的重心放在除了传输协议之外的其余部分,对 WebRTC 可以说一知半解。

Later, while developing a remote desktop product at Xuehayou, I discovered that employing an RTC SDK could significantly enhance audio quality. However, this RTC SDK was developed by another team within the company, and given the large number of developers involved, it was presumably a substantial library. My focus was primarily on aspects other than the transmission protocol, and thus, my understanding of WebRTC was, at best, rudimentary.

现在,既然要做自己的远程桌面产品,就不得不亲自面对 WebRTC。当然,前公司的 RTC SDK 肯定是不能用的,因为它以 Google WebRTC m89 为基准,这个库,稣认为太复杂了,没那么多时间去重新整理一遍。所以稣选择了一个比较小型但新颖的库,作为熟悉它的试验性项目,“声音共享”就诞生了。

Now, as I embark on creating my own remote desktop product, it is imperative that I confront WebRTC directly. Naturally, the RTC SDK from the previous company is out of the question for use, as it is based on Google WebRTC m89—a library I consider to be overly complex, and one that I simply do not have the time to reorganize from scratch. Therefore, I opted for a smaller yet more innovative library as an experimental project to familiarize myself with it, and thus, SoundShare was born.

开发过程 Development Process

首先在 Copilot 和 DeepSeek 的帮助下,理清了许多 WebRTC 的概念和原理,后来在 vcpkg 里搜索 rtc,对比了几个库,做好库的选型。

Initially, with the assistance of Copilot and DeepSeek, I clarified many concepts and principles of WebRTC. Subsequently, I searched for ‘rtc’ within vcpkg, compared several libraries, and finalized the selection of the appropriate library.

接下来,考虑先做个小工具练练手,不然直接把一个陌生的库上到 RegameDesk,万一后面发现不好用,岂不是要返工?

Next, I contemplated starting with a small tool to practice, reasoning that directly integrating an unfamiliar library into RegameDesk could lead to rework if it later proved unsuitable.

刚好,稣的笔记本扬声器很差,另一台迷你主机干脆就没有扬声器,还好又有一台脑波退下来的 iPhone 11 Pro Max,外放效果特别好,于是想到可以做个串流工具,让 iPhone 当笔记本的扬声器。

Coincidentally, my laptop’s speakers are subpar, and my mini PC lacks speakers altogether. However, I happen to have a decommissioned iPhone 11 Pro Max from my wife, which boasts excellent external audio performance. This led me to conceive of a streaming tool that would allow the iPhone to serve as the laptop’s speaker.

这里不得不吐槽一下 iPhone 11 Pro Max 的蜂窝信号,真的太差了,所以它基本上就是为 WiFi 而生,十分适合用来做"声音共享"的测试设备。

Here, I must vent a bit about the iPhone 11 Pro Max’s cellular signal—it’s truly abysmal. As a result, it’s essentially designed for WiFi, making it an ideal candidate for testing SoundShare.

虽然这本是一个练手的项目,但稣将它发布给一位朋友时,他竟然打赏了一块钱,于是干脆加点码,​做好一些!

Although this was initially a practice project, when I shared it with a friend, he unexpectedly tipped me one CNY. This gesture motivated me to enhance the project further and refine it to a higher standard!​

项目地址 Project Location

https://github.com/RegameDesk/sound_share

https://gitee.com/RegameDesk/sound_share

RegameDesk 开发笔记【2】Avro

数据交换采用 Avro。但在开始介绍之前,先进入大家最喜欢的答疑环节。

答疑

  1. 稣这是创业了吗?

没有。创业需要能赚钱的产品,还需要有投资(即,不是赚市场的钱,就是赚投资人的钱),稣两者都没有。再说创业肯定都是偷偷摸摸的。单纯就是回报社会主义社会。

  1. 不赚钱,你是 SX 吗?

众所周知,稣使用自然码双拼,普通的拼音简写早就看不懂了。SX 是啥?

经验

几乎在每家公司都用过 Google Protocol Buffers,除了它还没被开发之前。在网稣时,不仅大家推荐稣(在 Linux)使用 ProtoBuf,稣也推荐大家(在 Windows)用 ProtoBuf。但是在做远程桌面时,却和搭档很默契地不用它!最神奇的是,我俩一个写客户端,一个写服务端,期间也没对过具体协议,联调时数据结构居然 100% 一样,一跑就通。

当然这在工程上,并不是什么好事!因为这说明用的就是最简单的封装,相当于没用数据交换库,纯手工打造。要说为啥,那肯定就是偷懒,当时在 Windows 上使用 PB 还比较麻烦,为了“快速实现”就先牺牲扩展性。

到金斗云时,有的工程不用 ProtoBuf,有的用。原本设计上是:网络通信用 ProtoBuf,IPC 则用 FlatBuffers。但由于采用驱动抓图的方式,避开了 IPC 的使用(具体原因以后分析),所以其实 FlatBuffers 没用上。想用 FlatBuffers 是因为它效率高,但又因为它并不安全,所以不能用于网络上,只能用于 IPC。

在雪蛤油时,采用 ProtoBuf 是原有团队的积累,自己也熟悉,没理由不继续用。但发现两个版本的 Debug 版都会被 Boost.Test 报内存泄漏,这时候就凸显库臃肿的坏处了……ProtoBuf 这么大坨,根本没人愿意去分析,如果升级版本,又会出现两份(因为别组开发的 DLL 库里也用,稣只能升自己这边的,那边的他们不升),最后,看着没啥表面问题就直接弃疗。

分析

目前,RegameDesk 采用 Apache Avro 1.12.0。实际上,但凡有点名气的数据交换(或者叫数据序列化)格式和库都被研究得很多了,大可不必自己去选型,看看论文即可。比如这篇:[2201.03051] A Benchmark of JSON-compatible Binary Serialization Specifications

在大部分的对比项中,Avro 和 Microsoft Bond 常常是前两名。

ASN.1 也挺极致的,但它不好用。

Bond 的代码里有 Haskell,学习成本高于只有纯 C++ 的 Avro。而且它已经快停止维护了,详见 Bond project ending March 2025 #1215

简单看过 Avro 的代码,里面有 Zigzag 算法,用于压缩整形,这可太数学、太极致了,是基于统计学的——0 附近的数字被用得多,所以它们用更少的字节来表示。能考虑到这种点,光是想象中,基本上性能就不会落后(当然前面的论文也有实际验证)。

另外,Avro 本身很小巧,第三方依赖主要是 Boost.Iostreams,但 Boost 本来就是稣惯用的库,可以说维护上毫无压力。相对的,ProtoBuff 还带着不熟悉的 abseil,二进制整体加起来大 Avro 好几倍。以下对比基于 vcpkg 默认配置的编译结果:

文件 大小
ProtoBuff Lite abseil_dll.dll, libprotobuf-lite.dll 3.46MB
ProtoBuff abseil_dll.dll, libprotobuf.dll 14.9MB
Avro avrocpp.dll, boost_iostreams-vc143-mt-x64-1_86.dll, zlib1.dll, bz2.dll, liblzma.dll, zstd.dll 1.84MB

(实际上 4 个压缩库是能优化掉其中不用的几个,即还能更小)

使用案例

在设计上,RegameDesk 支持 4 种操作模式:

模式 具体功能
Assist 协助模式:适用于服务器前有人在操作的场景
1. 屏幕拓扑、分辨率等设置均以服务器为主,不改服务器的任何配置
2. 不会创建虚拟显示器
3. 允许多个此模式的客户端;兼容其它模式
Control 控制模式:适用于服务器为物理机,尤其是办公机
1. 屏幕拓扑、分辨率等设置均以客户端为主,将客户端的配置同步到服务器
2. 必定会创建虚拟显示器
3. 会断开服务器上所有物理显示器,防止服务器侧隐私泄露
4. 会使服务器静音,防止服务器侧隐私泄露,而且会阻止用户取消静音
5. 退出时,还原服务端设置
6. 只允许一个此模式客户端;与 headless 模式互斥
Headless 无头模式:适用于服务器不插显示器的场景(物理机或虚拟机皆可)
1. 假定服务器只有虚拟显示器,如果有物理屏幕,则断开,防止服务器侧隐私泄露
2. 屏幕拓扑、分辨率等设置均以客户端为主,将客户端的配置同步到服务器
3. 会使服务器静音,防止服务器侧隐私泄露,但不会阻止用户取消静音
4. 退出时,不还原服务端任何设置
5. 只允许一个此模式客户端;与 control 模式互斥
View 仅查看模式:不能操控
1. 屏幕拓扑、分辨率等设置均以服务器为主,不改服务器的任何配置
2. 不会创建虚拟显示器
3. 允许多个此模式的客户端;兼容其它模式

用 Avro 来表示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"type": "record",
"name": "OperationMode",
"fields": [
{
"name": "mode",
"type": {
"type": "enum",
"name": "Mode",
"symbols": [ "kAssist", "kControl", "kHeadless", "kView" ]
}
}
]
}

人类都说:JSON 的可读性最好。是这样没错!

RegameDesk 开发笔记【1】立项

故事

在遥远的古代,那时候还没有掐鸡,甚至搜索引擎都才刚刚起步,热爱学习的稣还只能去图书馆查阅资料。机缘巧合下,稣发现厦门大学图书馆不仅有很多“砖头”(工具书),还可以免费上机。是的,免费,当时“白嫖”这词还没被发明。

当年的厦门大学图书馆可以说是高科技满级,直到今天看也并不落后——居然是云架构!瘦终端远程登陆到 NT4 的终端服务,这套东西和今天人们用 Windows 自带的 mstsc.exe 或macOS 上的 Microsoft Remote Desktop/Windows App,远程登录 Windows 2000/XP/2003/Vista/7/8/8.1/10/10……可以说,根本就是一样的。

总之,当时天上打了个雷,震惊程度和高中时代看《黑客帝国》不相上下。

上班后,虽然做过很多产品,但潜意识里一直被远程桌面吸引,十几年的观测后,终于慢慢靠拢。目前,稣在三家公司做过远程桌面。

经验

注:以下公司名都是虚拟的,不会有雷同。

第一家网稣,开发起码有 6 名,稣当时还是救火员到处帮忙,所以只负责保驾护航,没多久就去其它部门,再后来就离职了。当时不算核心开发,只是一起搞了个 Demo,验证我们能做出来原型,而且给网吧内网用问题不大。但要做精,做在互联网上用的,火候差很多。比如当时直接用 Live555 做传输库,没人专项优化 rtsp 协议,抗弱网能力这个指标从没被关注到。外设也只支持键盘、鼠标,还是最基本的 SendInput 重放。

第二家金斗云,开始时就稣一人同时做云游戏两个版本和一个远程桌面 Demo,以及另一些项目……后来远程桌面多了一个十几人的团队,至今已经做了多年。这一次,稣的重心是想放在虚拟驱动上的,研究了各种驱动框架,基本知道如何做好一个远程桌面产品。不过由于家里太穷,两年没加薪,脑波意见很大。部门的新领导正好有意让稣去深圳发展,干脆看看机会,结果有个猎头正好找稣说有个远程桌面的岗位,而且是给公司的员工做的,不需要考虑盈利!无论从事业还是家庭角度看,都合算,于是跑了。

第三家雪蛤油,入职时已经有大佬做了一个 Demo,完成度和网稣的那个差不多,只是 UI 丑了很多,毕竟网稣的界面是专业的 DUI 开发 2 人做的。进来前妄想是这公司有钱,人应该比较多,能专心做核心驱动,结果正好相反。一进来就发现开发,包含稣就 2 名,招聘没几天领导就宣告不招了。整个开发周期就 2 名全职 C++ 开发,一名外援 C++ 和一名外援前端。然而,团队又是有许多技术积累、管理手段和周期要求的,按各项最优的来阻力大,所以最终是留着一半原有风格,局部替换为稣认为的足够极致的解。最大收获是产品运营起来后,发现用户的各种毛病怎么防治。毕竟这是一个系统级软件,任何用户自己的问题,都可以被用户怀疑是“软件的 bug”,防范“瓜田李下”是必备的。

新愿景

RegameDesk 将从零开始,吸收在之前三家公司经历过的教训,每项关键点都追求安全和极致。框架上,会比较倾向金斗云时的设计。

产品理念:**为全宇宙打工,搞一个程序员认为安全的软件。**具体说,市面上,除了微软的远程桌面,估计还没有其它能被程序员们认为安全。大部分产品,都有账户体系,会维持和厂商服务器连接,数据可能经过厂商中转。它们可能自称是加密的,但密钥都不能自定义,厂商到底能不能解密都难讲。

罗老师:稣真不是为了赚钱,这样的产品能赚钱才怪。

不建议使用 using namespace

故事

最近稣在梳理一份 Google C++ Coding Style 的更严格版本,其中,有一条是“在大范围禁止使用 using namespace”,结果获得 100% 员工的支持。员工人数:1!

分析

具体来说,大范围指的是比函数更大的范围。通常的 Coding Style 可能会提到:禁止在头文件里使用 using namespace,而这里的禁止显然涉及的地方更多。禁用它的理由通常有以下这些:

  1. 降低可维护性:using namespace 会降低代码的可读性,因为它隐藏了实体的来源,读者需要额外的工作来确定一个名称属于哪个命名空间。如果后续添加或修改了同名的实体,可能导致编译失败,甚至可能引入难以发现的错误。

  2. 降低编译性能:using namespace 可能会导致编译器查找符号时的性能下降,因为它需要在所有导入的命名空间中搜索。

同时,我们放开了例外的限制,没错,您可以在任何地方,比如头文件里,使用 using namespace 各种 std::literals

如果您觉得不使用 using namespace,会导致需要写大量很长的 namespace,那您应该使用 namespace 定义别名来缩短,如果是本项目的 namespace 倾向于使用最短的简称。例如:

1
2
3
4
5
6
namespace d = umutech::regame::diagnostics; // 自己的命名空间,尽量短
namespace fs = boost::filesystem;
namespace nw = boost::nowide;
namespace po = boost::program_options;

using namespace std::string_view_literals; // 例外

microsoft/proxy 学习笔记

1. 用于啥需求?

实现多态性。它允许统一管理和操作多种子类对象,在传统方法中,这通常通过基类指针来实现。

2. 何时使用 Proxy?

每当你在代码中使用 virtual 关键字定义虚函数时,可以考虑使用 Proxy 库作为替代方案,以提高性能和简化代码管理。

3. 有时候 CRTP 也可以替代 virtual,CRTP 也能用 Proxy 替代吗?

虽然 Curiously Recurring Template Pattern (CRTP) 可以在某些情况下替代 virtual 函数,但 Proxy 并不是用来替代 CRTP 的。如果你的目标是代码复用,而不是统一管理多种子类,那么 CRTP 仍然是更合适的选择。例如,如果你有多个项目使用相似的代码段,并且这些代码段被封装在一个基类中,通过 CRTP 实现代码复用,而这些子类只在各自的项目模块中使用,那么继续使用 CRTP 是合适的,使用 Proxy 则不太适合。

Windows 平台编译 FFmpeg

需求

  • FFmpeg 7.0 开始已经支持 D3D12VA,可以自己编译一份采用 LGPL 协议的定制版了。

  • 以前都使用 ShiftMediaProject,现在想学习一种新的编译方式。

解决

这次采用较为普遍的方式:在 MSYS2 环境下编译。

1. 下载、安装

官网下载,并且官网有 Installation 说明。安装完 MSYS2 本身后,需要在 MSYS2 环境下安装编译 FFmpeg 所需的工具:

1
pacman -S autotools yasm

当然,您还需要下载 FFmpeg 代码,目前合适的版本为 7.1。下载完,解压到合适的目录,比如 D:\devel\ffmpeg,这样代码目录就是 D:\devel\ffmpeg\ffmpeg-7.1

2. 启动编译环境

为了编译 x64 版本,运行 x64 Native Tools Command Prompt for VS 2022。然后进入 MSYS2 安装目录,输入以下命令:

1
msys2_shell.cmd -use-full-path

此时获得一个新打开的 MSYS2 实例窗口,后面的命令在这里输入(即,不需要 x64 Native Tools Command Prompt for VS 2022 窗口了)。

3. 编译

由于稣只需要 D3D12VA 对 HEVC 的支持,所以编译其实很快,命令行如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cd /d/devel/ffmpeg/ffmpeg-7.1
export OUT_DIR=../build
./configure \
--target-os=win64 \
--arch=x86_64 \
--toolchain=msvc \
--enable-shared \
--disable-all \
--disable-autodetect \
--enable-swresample \
--enable-avcodec \
--enable-encoder=hevc_d3d12va \
--enable-decoder=hevc \
--enable-d3d12va \
--enable-hwaccel=hevc_d3d12va \
--prefix="$OUT_DIR"

make -j
make install

Windows 平台用 VBS 保护私钥安全

问题

之前稣写过《macOS 上用触控 ID 安全登录 SSH》和《Debian 12 上用 TPM 2.0 安全登陆 SSH》,为何没 Windows 版?

分析

当然有!只是太简单,稣以为大家都会……直到今天才发现不少人都不懂保护自己的私钥,真以为没人惦记你的三千块钱存款吗?

解决方案

网络上能轻易找到的一种解决方案是使用虚拟智能卡,它使用 TPM 2.0 保护私钥,而且使用时需要输入 PIN,安全性是可以的,唯一的问题就是很麻烦。

详情参考:Secure SSH Access with TPM2-Backed Key

稣的解决方案基于 VBS,全称 Virtualization-based Security那啥,不是 VBScript 这种已经去世几十年的东西。 VBS 的底层有一部分是 TPM 2.0,安全性有保障。

1. 离线产生私钥

  • 专门买一台百来块的擸𢶍小主机,从不接入网络,是从不,连局域网都不。

  • 产生密钥导出时需要使用强密码保护。

  • 将加密的密钥文件(.pfx)复制到一个安全的 U 盘,但是这个 U 盘丢了也不怕,因为擸𢶍小主机上还有密钥,而且 U 盘里的文件也有密码。

当然,如果您觉得私钥丢了也无所谓,那可以不用保存私钥副本,只需要保证私钥不被偷。

2. 导入使用密钥的机器

这步隐含“使用密钥的机器”是不那么安全的,所以需要处处防御:

  • 机器拔网线、断 WiFi,重启,进入安全模式。

  • 插入 U 盘,快速导入私钥,导入时必须选择“使用虚拟化安全保护私钥(不可导出)”,快速移除 U 盘。

  • 重启。

导入时有几个可选项:

  • “启用强私钥保护。如果启用这个选项,每次应用程序使用私钥时,你都会收到提示。”稣一般也开启,以便知道哪些应用不老实。

  • 密码。如果机器只有自己使用,可以不用设置,把“此密钥需要密码”点掉。即使不设置密码,被 VBS 保护的私钥也不可能被偷走。

后记

本文没有任何命令和图片,单纯传授心法,因为绝大多数的观众老爷们根本没有安全需求,贴出图片也是浪费时间和流量。

Diffie-Hellman 密钥交换协议

Diffie-Hellman 密钥交换协议,允许双方在没有预先共享秘密密钥的情况下通过不安全的通信渠道建立共享的秘密密钥。这个名字来源于发明者 Whitfield Diffie 和 Martin Hellman。

Diffie-Hellman 密钥交换原理简介

Diffie-Hellman 密钥交换(通常简称为 DH)依赖于数论中的一个事实:在一个有限字段上,给定生成元 g 和素数 p,计算 $g^a \mod p$ 相对容易,但是尝试反向计算,即已知 $g^a \mod p$ 求 a,则是计算上不可行的,这种问题被称为离散对数问题。

流程

  1. 参数生成和共享:首先,双方同意使用一个公共的大素数 p 和基数 g(这两个值就是所谓的 “DH Parameters”),g 通常是 2。

  2. 私钥和公钥生成:

  • 每一方选择一个私有(不与对方共享)的随机数,作为自己的私钥。设发送方私钥为 $\alpha$,接收方为 $\beta$。
  • 然后计算公钥 $A = g^\alpha \mod p$(发送方)和 $B = g^\beta \mod p$(接收方),并互相交换这些公钥。
  1. 计算共享秘密:
  • 发送方计算 $S = B^\alpha \mod p$。
  • 接收方计算 $S = A^\beta \mod p$。
  • 如果计算正确,双方将得到相同的值 S,该值可以用作通信双方的共享秘密。

由于反向计算离散对数是计算上不可行的,因此第三方无法仅仅通过拦截 A 和 B 来确定共享秘密 S,除非他们能够解决离散对数问题,这在实践中是非常困难的,尤其是对于足够大的 p。

使用场景

Diffie-Hellman 主要用于在不安全的通道上安全地交换密钥,这些密钥之后可以用于加密后续通信。例如,在 TLS/SSL 握手过程中就可能使用它来建立会话密钥。

密码套件和 DH 参数

TLS 支持多种类型的密码套件,这决定了密钥交换、消息认证、加密和密钥材料生成算法。当涉及到 Diffie-Hellman 密钥交换的密码套件时,它们通常会分为两类:

  • 传统 DH 密钥交换:在这种情况下,服务器和客户端事先同意一组 DH Parameters(公共的大素数 p 和基数 g)。服务器在 TLS 协议的相应阶段将这些参数发送给客户端。然后,服务器和客户端各自生成一个随机私钥,并基于此私钥和接收的参数计算公钥,再互换公钥以建立一个共享秘密。由于这些参数(特别是素数 p 和基数 g)是公开交换的,所以对安全性几乎没有影响,关键是私钥的私密性。

  • 椭圆曲线 Diffie-Hellman 密钥交换(ECDH):与传统 DH 类似,但使用椭圆曲线数学而非模幂运算。在 ECDH 中,参数是指椭圆曲线的定义,以及用于生成公私钥对的基点。这些参数同样需要在通信双方间共享或事先约定。

SSL/TLS 握手和 DH 参数

在实际的 SSL(或 TLS)握手过程中,以下步骤涉及 DH Parameters 的使用:

  1. 服务器 Hello 消息:服务器选择一个密码套件,该套件可能包含 DH 密钥交换方法。

  2. 服务器 Key Exchange 消息:如果所选的密码套件基于 Diffie-Hellman 密钥交换,服务器将在这个消息中发送 DH Parameters(对于 ECDH,发送椭圆曲线参数和服务器的公钥)。对于某些 DH 套件,服务器的公钥可能已经在一个证书中,这种情况下,参数可能不需要在这条消息中发送。

  3. 客户端 Key Exchange 消息:客户端接收到服务器的 DH Parameters 后,生成自己的 DH 密钥对,发送自己的公钥给服务器。

  4. Premaster Secret 的计算:一旦双方都有了对方的公钥,它们就可以独立计算出一个共同的秘密值(Premaster Secret),接下来使用这个秘密值生成会话使用的加密密钥。

在整个过程中,即使 DH Parameters(素数 p、基数 g 和相应的公钥)是公开的,由于 Diffie-Hellman 问题的难解性,攻击者不能轻易地计算出共享秘密,从而保证了密钥交换过程的安全性。

注意

虽然 Diffie-Hellman 本身是一种安全的密钥交换协议,但它不提供身份验证功能。因此,在某些实现中,它通常会与数字证书或其他身份验证机制结合使用以防范中间人攻击。

真实的程序员情商高得可怕!

情绪商数(Emotional Quotient,EQ)是指一个人对自己和他人情感的感知、理解和应对能力。但长期以来,在传统文化浸濡下,很多人以为情商是处事圆滑,甚至是 PUA 那套。但凡有人拒绝被 PUA,就会迎来一顿“情商低”的舆论攻击。

程序员,就是最大的受害者群体,可能有之一,但几乎……没有!

以下从四个模型五个视角来说明——其实天生的程序员,情商高得可怕!

四个模型

  1. 情绪知觉(Perceiving Emotions):程序员最善于观察,所以很容易知道对方是否被激怒,随时会调整自己的话术,既不能让对方被气死,更不能让对方动手打人。

  2. 情绪理解(Understanding Emotions):程序员最善于理解前向传播和后向传播。对方如何被激怒,甚至气得说不出话的,都是全程跟踪和拿捏的。甚至还能为下一次战争调整模型参数。

  3. 情绪运用(Using Emotions):程序员最善于隐藏情绪。隐藏实力、知己知彼,才能百战不败。打不过时,有的脾气爆发,辞职走人,有的留下一堆诗山,辞职走人。

  4. 情绪调节(Managing Emotions):程序员最善于保持脸瘫,情绪一直良好。

五个视角

  1. 自我意识(Self-awareness):程序员平均智商高于常人,自我意识同样更强。

  2. 自我管理(Self-management):程序员自我驱动力很强。

  3. 社交意识(Social awareness):世人对程序员最大的误解在此。其实程序员的社交意识特别强烈,不然为何有世上最大的同性社交平台?程序员们只是排斥无效社交而已,人们理解的那种酒桌文化就是程序员认为的无效社交。但如果哪天需要创业,需要演戏,也不是不行哦。稣曾经见过某些隐藏实力的程序员爆发惊人的酒量,您想说这是个例?非也,都是人,这种个例的比例不会低于整体人群的比例。

  4. 关系管理(Relationship management):程序员特别能管理关系!能力差的,很容易就被管理出局。能力强的也特别突出,一般用来搞上面的人,争取气死一批是一批。

  5. 自我激励(Self-motivation):程序员特别擅长跳槽加薪……另一方面,别人眼中枯燥无味的东西,程序员乐此不疲,不也是自我激励吗?

真实案例

程序员天生会《孙子兵法》,而且运用自如。下面看看一些经典案例。

  1. 产品经理:理解能力怎么这么低下?

故意的。他是觉得产品经理没干啥活,故意不懂,让产品经理把事情理清楚,流程画完整再来。你想浪费时间争辩,我也正好休息一会儿,发泄一下,争取当场气死你。

  1. 领导:你怎么把问题又抛给我?

原因大约有三类:一是你管太多了。疑人不用,用人不疑。管是没问题的,不要管技术细节,你真这么懂,自己做?让程序员花时间解释,你怎么不自己看代码?二是你交代的需求不明确,只能你自己选型。三是你带着偏好,比如你喜欢某方案,一个劲提起,那不然你随便实现了?

  1. 写个文档都写不好?

两类比较大的可能:一是写的这类文档不应该程序员写,某些角色不够专业或上心,应该自己写的,推给程序员。二是这是机密技术,怎么能写明白呢?杀人诛心啊!还是装傻保命吧。

  1. 怎么没有架构设计文档?是不是偷懒?

相反,正是因为实在,所以“对不起,没有!”。这通常是一个不合事宜的问题,尤其是在敏捷开发的团队。一些业务为主的产品,有合格的需求文档基本就行,开发者每个都是架构师。而以技术深度著称的产品,架构通常是固化的,或者有知名的最优解,没得选,没人只是架构师。而以量级大为特色的产品,基本就是抄,架构是现成的,比如国产操作系统。总之,在真实的商业软件里架构设计是一个特别尴尬的东西,往往是后补上的。但是,万一真的有一个架构师职位的人,一定要让他事先写好,以后实现发现不对,再来讨伐他。em……花时间写这东西,还可能被批判,情商高得可怕的程序员,怎么会写呢?

  1. 测试环境好好的,怎么到了用户环境就出 bug?

这是为了 GDP、为了民生考虑的伟大情操!业界不成文的规则:开发阶段只解决理想环境的需求,测试阶段解决产品、测试人员的饭碗,发布阶段解决运营人员和自己后续的饭碗。开发周期短,其它周期就长,程序员是精于计算的,不打没胜算的仗。如果不能养活一群人,那和当畜生直接给吃掉有啥区别?