您正在查看: tian051011 发布的文章

Windows Developer PowerShell 编译 Qt Quick Designer components 时遇到的奇怪报错与解决方式

需编译的项目

https://github.com/qt-labs/qtquickdesigner-components

编译环境

Windows 11 23H2
Developer PowerShell for VS 2022
Qt 6.8.0 Community

编译命令

PS D:\Projects\qtquickdesigner-components-qds-dev-4.6\qtquickdesigner-components_build> cmake -G"Ninja" -S ../ -DCMAKE_PREFIX_PATH=D:\\Qt\\6.8.0\\msvc2022_64 -DCMAKE_MAKE_PROGRAM:STRING=D:\\Qt\\Tools\\Ninja\\ninja.exe

错误提示

CMake Error at CMakeLists.txt:55 (find_package):
  Could not find a configuration file for package "Qt6" that is compatible
  with requested version "".

  The following configuration files were considered but not accepted:

    D:/Qt/6.8.0/msvc2022_64/lib/cmake/Qt6/Qt6Config.cmake, version: 6.8.0 (64bit)



-- Configuring incomplete, errors occurred!

这个报错非常有迷惑性,初看会以为是构建时找不到对应的 CMake 依赖版本,虽然最后确实是和这点有关。

解决方案

换成默认 x64 编译器的x64 Native Tools Command Prompt for VS 2022

原因

Developer PowerShell for VS 2022环境默认是 x86 的,CMake 无法接受 x64 的 Qt 库作为可用版本。

如果你的 Qt 程序修改后编译出现了无法理喻的奇怪错误,也可以考虑删除原有的构建文件夹,从头构建,注意不是之前也遇到了这种问题,最后发现可能是旧的编译产物和新的冲突了。

使用 USB IP 将 Windows 下的 USB 设备转发至远端 Linux 设备

前言

项目需要在 Linux 私有云虚拟机上开发,但因为是嵌入式项目,涉及到烧录固件之类的操作,之前都是在 Linux 系统上编译好固件,之后用 WinSCP 下载到作为终端的 Windows 笔记本上,把开发板或者模组接在在笔记本上用 J-Flash 烧录,比较麻烦。如果是在本机开发,可以直接用 VSCode 开发环境一键烧录,调试也会方便点。开发板与模组都通过 J-Link 接 USB 与电脑通讯,于是就在想能不能将其转发到 Linux 开发机。之前摸索了一段时间,研究出了下面这个可行的方案。
首先,从原理上讲,这个方案用的就是USB/IP。关于 USB/IP 技术就不做过多介绍了,一言以盖之就是将 USB 协议数据通过 IP 网络传输,感兴趣的话可以看下项目主页

Windows 笔记本端

我在 Windows 上找到了两个可用的USBIP实现,usbip-winusbipd-win。前者有客户端与服务端,但已经不再维护,且需要打开 Windows 的测试模式用于安装相关驱动,有一定风险,不过很多能在网上找到的老文章还在推荐它。我尝试了其服务端实现,但未能成功。后者仅有服务端,但是是微软官方文档推荐的将 Windows 上的 USB 设备转发至 WSL 的实现,且在持续更新,不过只支持 Windows 10 及以上系统版本。目前我们的需求只需要 Windows 笔记本端作为服务端,也就是向外提供 USB 设备的,最后我也选择了usbipd-win。
我的笔记本上的操作系统为 Windows 11 23H2。
首先前往项目官方仓库Release页面下载最新版本的 msi 安装包,并安装到 Windows 电脑上,有可能需要重启。
安装完成后,启动一个管理员权限的终端(CMD 或者 Powershell 都行),确定服务已启动:
sc.exe query usbipd
如果安装正常的话,STATE 应该是 RUNNING。
接下来将想转发的设备插入 Windows 设备的USB接口,有几个就接几个,用usbipd list命令列出所有设备。下面是我在电脑上接入JLink-OB与USB转串口设备后该命令的输出。

PS C:\Users\tian051011> usbipd list
Connected:
BUSID  VID:PID    DEVICE                                                        STATE
2-1    10c4:ea60  Silicon Labs CP210x USB to UART Bridge (COM7)                 Not shared
2-2    1366:0105  JLink CDC UART Port (COM8), J-Link driver                     Not shared
2-3    1532:009a  USB 输入设备, Razer Pro Click Mini                                Not shared
2-6    30c9:0057  Integrated Camera, Integrated IR Camera, APP Mode             Not shared
2-10   8087:0026  英特尔(R) 无线 Bluetooth(R)                                        Not shared

接下来使用usbipd bind命令设置你想要转发的设备,你需要用到上面这个列表里的 BUSID:

PS C:\Users\tian051011> usbipd bind --busid 2-1
PS C:\Users\tian051011> usbipd bind --busid 2-2

如果转发被正常设置了,命令执行之后你应该不会看到任何输出。这时候再次执行usbipd list命令,可以看到设备的状态已经变成了 Shared:

PS C:\Users\tian051011> usbipd list
Connected:
BUSID  VID:PID    DEVICE                                                        STATE
2-1    10c4:ea60  Silicon Labs CP210x USB to UART Bridge (COM7)                 Shared
2-2    1366:0105  JLink CDC UART Port (COM8), J-Link driver                     Shared
2-3    1532:009a  USB 输入设备, Razer Pro Click Mini                            Not shared
2-6    30c9:0057  Integrated Camera, Integrated IR Camera, APP Mode             Not shared
2-10   8087:0026  英特尔(R) 无线 Bluetooth(R)                                   Not shared

对特定设备的转发只需要设置一次,之后只要此设备连入,即可被转发。注意虽然我们设置转发时使用的参数是 busid,但并不是之后接入这个bus的设备就会被转发,程序不过是采用这个数据读取 VID 与 PID,被设置转发的只能是由这两个参数标记的某个设备。Windows 端的操作结束了。

Linux 服务器端

首先,你需要确保 Linux 服务器可以通过 IP 网络连接到 Windows 端,可以是通过局域网,也可以是 SSH 端口转发或者公网 IP(这种情况会有安全风险)。请将下文的192.168.123.123替换成你的 Linux 服务器访问 Windows 主机的IP。
Linux端的操作系统为Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-113-generic x86_64)
首先需要安装usbip相关的软件包sudo apt install linux-image-extra-virtual linux-tools-generic hwdata
然后加载usbip相关的内核模块sudo modprobe vhci-hcd
最后就是将Windows端的设备转发到Linux服务器了sudo usbip attach --remote=192.168.10.16 --busid=2-1,有几个就转发几个。
如果全程没有出错,此时用lsusb命令就能看到被转发过来的设备了。

ubuntu@tian-server:~$ lsusb
Bus 003 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 002 Device 012: ID 1366:0105 SEGGER J-Link
Bus 002 Device 011: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub

之后就可以在你需要使用的应用中使用这个 USB 设备了,对于我来说,就是用 VSCode 远程开发环境操作被转发过来的设备。

DNS也有用TCP?做个实验来验证吧

起因

之前在面试时,被问到了DNS底层采用了什么协议。“UDP。”我脱口而出。面试官追问:“还有么?”我又想了想,说DoT和DoH会用TCP协议。面试官对我的回答并不是特别满意,说普通的DNS查询也支持用TCP协议。
面试结束之后,查询了相关资料,发现理论上标准的DNS协议确实也支持TCP传输,但一般的DNS客户端会比较少遇到这种情况,因为:

  1. 只有在解析配置过于复杂,或者记录值过长,使得没有自带分包功能的UDP协议无法承载的情况下,才会用TCP查询。
  2. 很多DNS服务提供商不支持TCP查询。

既然已经知道了对应的原理,有必要做个小实验来验证一下。

实验

我采用的系统环境是Windows 11 23H2,运行着Wireshark 4.2.4。为了采集DNS协议的包,我们可以使用port 53或者dns过滤器。

UDP协议的普通DNS查询与响应

开始捕获后,我们可以打开终端,用nslookup命令查询任意常见域名的对应IP地址。我这里用了自己博客的域名。

PS C:\Users\tian> nslookup tian051011.me
服务器:  RT-AC1900P-B8B8.lan
Address:  192.168.50.1

非权威应答:
名称:    tian051011.me
Address:  185.186.147.19

如果没有出意外的话,Wireshark应该就捕获到我们的DNS查询的查询和响应了。如下图,可以看到此时的DNS查询与响应确实采用了UDP协议。
普通的DNS查询采用了UDP协议

普通的DNS响应也采用了UDP协议

TCP协议的DNS查询

首先我们需要寻找或构造一个够长的记录,让UDP塞不下这个记录,服务器才会给我们发TCP格式的响应。
这里我给tian051011.me这个域名增加了test主机记录,记录类型为TXT,记录内容是768个1,实测DNSPod是支持这么长的TXT记录的。
长长长长的记录
让我们来看看对这个域名进行DNS查询会发生什么吧。

PS C:\Users\tian> nslookup -type=TXT test.tian051011.me
服务器:  RT-AC1900P-B8B8.lan
Address:  192.168.50.1

非权威应答:
test.tian051011.me      text =

        "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
        "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
        "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
        "11"

nslookup成功查出来了我们设置的TXT记录。让我们看看Wireshark那边都捕获到了些什么。
Wireshark捕获到了TCP格式的DNS查询与响应
我们可以发现客户端首先发送了UDP格式的查询,并得到了UDP格式的响应,但这个响应很特殊,里面并没有Answers,Truncated标记位也告诉我们这个响应是被截断的。
响应被截断
接下来,客户端就用TCP发送了查询请求,并得到了TCP包中的完整响应。
TCP DNS查询请求

TCP DNS响应

不支持TCP协议的DNS服务器

根据我的实测,不同的DNS服务提供商对于这种响应超长的查询会有不同的响应,例如114DNS会直接响应“无可用记录”、阿里云公共DNS会出现“未定义错误”,这也反映出很多DNS服务器的实现可能不符合RFC标准。
我这里采用了DNSPod Public DNS,让我们来看看这种情况下对test.tian051011.me进行TXT查询会发生什么吧。

PS C:\Users\tian> nslookup -type=TXT test.tian051011.me 119.29.29.29
服务器:  pdns.dnspod.cn
Address:  119.29.29.29

tian051011.me   nameserver = f1g1ns1.dnspod.net
tian051011.me   nameserver = Invalid Name at offset 80!


*** Error: record size incorrect (-408812189280 != 10)

tian051011.me   nameserver = f1g1ns1.dnspod.net
tian051011.me   nameserver = Invalid Name at offset 80!


*** Error: record size incorrect (-408812189280 != 10)

*** pdns.dnspod.cn 找不到 test.tian051011.me: Unspecified error

nslookup报错了,从报错信息我们可以大致了解到出现的问题是记录大小不对。那么在这种情况下,Wireshark捕获到了什么呢?
首先,查询和响应都没有出现TCP格式。

其次,DNS响应的记录是损坏的,Truncated标记位也没有被设置。

另一种情况-区域传送

其实,还有一种情况,DNS记录基本是在TCP协议下被传送的,那就是“DNS区域传送(zone-transfer)”。在这种情况下,一台备用服务器会使用来自主服务器的数据来刷新自己的域(zone)数据库。由于这种传送要求可靠性高且数据量大,一般会采用TCP协议,不过一般用户是接触不到这种情况的。目前暂时没有条件做区域传送的实验,可以先了解一下相关概念。

将博客Typecho版本更新至1.2.0,与这期间遇到的坑

前言

真的好久没有更新博客了!这自然有我个人最近想进行的文字表达比较少的原因,但也有生活与学习繁忙的原因。期间也发生了很多事情,我可能会把其中的一些整理后记在博客上吧。同样好久没有更新的,还有Typecho。今天久违的登录了博客的后台,吃惊的发现Typecho居然在4月1日发布了一个新版本1.2.0。不是愚人节玩笑,真是令人吃惊,要知道Typecho的上一个稳定版本1.1,也是本博客目前正在使用的版本是在2017年10月发布的,是将近5年前。而我的最后一篇博客,是在2019年10月发布的,是将近3年前。将Typecho的更新作为本博客重启的序幕,我觉得很合适。

准备工作

首先,我们来看看Tyepcho 1.2.0的安装环境要求。

PHP 7.2 以上
MySQL, PostgreSQL, SQLite 任意一种数据库支持,并在 PHP 中安装了相关扩展
CURL 扩展支持
mbstring 或 iconv 扩展支持

其中最大的变化是PHP版本要求从5.1飞跃到了7.2。其实更新PHP本来可能是这个任务中最难的部分,然而在一两年前左右,好像是为了部署FreshRSS,我已经把PHP从5.4更新到7.4了,还顺带把Typecho 1.1在PHP 7.4的报错修了下,所以这部分理论上不必再折腾了。但是,既然是时隔已久的更新,总是要彻底一点嘛!另外也是由于在VPS上,我使用了LNMP一键脚本,更新PHP差不多也是一个命令完事。

使用SSH连接到VPS后,先更新LNMP到1.8吧。

下载LNMP 1.8压缩包并解压:

[root@web ~]# wget http://soft.vpser.net/lnmp/lnmp1.8.tar.gz
[root@web ~]# tar -zxvf lnmp1.8.tar.gz

切换至新目录并执行更新脚本:

[root@web ~]# cd lnmp1.8
[root@web lnmp1.8]# ./upgrade1.x-1.8.sh

之后按任意键开始更新并等待更新完成。

LNMP更新完成之后是重头戏,更新PHP。不过LNMP脚本已经将其做的很人性化了,只需要在相同目录执行./upgrade.sh并按提示操作就行了。

由于编译更新PHP是一件耗时且不应中断的任务,我这里开了一个screen单独运行它,这样SSH断连脚本也不会终止了。

[root@web lnmp1.8]# screen -S upd_php
[root@web lnmp1.8]# ./upgrade.sh

之后按提示操作即可。这个升级脚本还可以用于升级Nginx,MySQL/MariaDB等,这里就不展示了。

开始升级

根据Typecho的文档,Typecho本身的升级还是很简单的,只需要删除/admin//var//index.php/install.php,之后把新版的这些文件复制进去就行了。我们照着教程来吧。

下载最新版的Tyepcho并解压至新建的目录:

[root@web ~]# wget https://github.com/typecho/typecho/releases/latest/download/typecho.zip
[root@web ~]# mkdir typecho-1.2.0
[root@web ~]# mv typecho.zip typecho-1.2.0/
[root@web ~]# cd typecho-1.2.0/
[root@web typecho-1.2.0]# unzip typecho.zip

备份旧版Typecho的文件:

[root@web typecho-1.2.0]# cd ..
[root@web ~]# mkdir typecho-1.1
[root@web ~]# mv -t typecho-1.1/ /home/wwwroot/tian051011.me/admin/ /home/wwwroot/tian051011.me/var/ /home/wwwroot/tian051011.me/index.php /home/wwwroot/tian051011.me/install.php

将新文件复制过去,别忘了更改权限:

[root@web ~]# cp -rt /home/wwwroot/tian051011.me/ typecho-1.2.0/admin/ typecho-1.2.0/var/ typecho-1.2.0/index.php typecho-1.2.0/install.php
[root@web ~]# chown -R www:www /home/wwwroot/tian051011.me/admin/ /home/wwwroot/tian051011.me/var/ /home/wwwroot/tian051011.me/index.php /home/wwwroot/tian051011.me/install.php

之后访问admin页面就可以完成升级了!

后台升级页面

当然在此之前最好按照提示备份一下。

后台备份页面

升级完成,与遇到的问题

升级完成之后,页面跳转回了后台。访问本博客,发现没有插件与功能出现异常。不过,后台却多了一个升级提示,而我们很明显已经升级到最新版了。

后台提示升级

查看/admin/index.php文件,可以发现这个升级提示的信息是通过浏览器的sessionStorage保存的。

$(document).ready(function () {
    var ul = $('#typecho-message ul'), cache = window.sessionStorage,
        html = cache ? cache.getItem('feed') : '',
        update = cache ? cache.getItem('update') : '';
    ……
    function applyUpdate(update) {
        if (update.available) {
            $('<div class="update-check message error"><p>'
                + '<?php _e('您当前使用的版本是 %s'); ?>'.replace('%s', update.current) + '<br />'
                + '<strong><a href="' + update.link + '" target="_blank">'
                + '<?php _e('官方最新版本是 %s'); ?>'.replace('%s', update.latest) + '</a></strong></p></div>')
                .insertAfter('.typecho-page-title').effect('highlight');
        }
    }
    ……

可能是上次获取的结果还在缓存里,清除一下对应的sessionStorage试试吧。要达成这一目的,最简单的方面是在该页面按Ctrl+F5进行硬刷新。

后记

时光的流逝真是令人感叹,我离开博客的这段时间也发生了很多事,很多事物都渐渐的改变了。但是,还是有些东西是不怕时间的。
我计划用自己近年来学到的知识继续改造本博客。如果真的有人用RSS订阅了本博客,或者刷收藏的时候看到了本文章,希望看到这里的你可以知道,这个博客还没有弃坑哦!

Surface Pro X上的WSL 2——安装、配置与体验

背景介绍

不久之前,我在闲鱼上花差不多4000元收了一台带键盘与笔的初代Surface Pro X,它的配置为微软与高通合作“研发”的SQ1处理器(实际上就是855 Pro Max),加上8GB LPDDR4内存,还有一块卖家后加的1T BC711固态硬盘。作为一台2019年生产的飘洋过海而来的美版机子,它已经经历了至少三代主人,电池效率居然还剩95%,外观状态也还不错,可以说是非常划算了。
我从当年第一代Surface发布开始就想体验微软的硬件设备,如今算是终于圆梦了。Surface Pro X轻薄美观,让人爱不释手,无风扇设计让它运行时没有任何噪音,虽然是近3年前的设备但依旧反应敏锐,可以说是WoA标杆级设备。
Windows 11的Windows On ARM体验对我来说可以说是非常惊喜,这台轻薄便携的二合一很好的填补了我的设备空白。我想在这台设备上运行的软件都可以完美运行,本来不期望在这台设备上能很好运行的软件它也可以运行,虽然性能一般,但也完全足够了。W11对触摸的优化也是大大增强,各类触摸手势的加入极大地提高了它在被触摸使用时的便捷度。我最喜欢的手势是从任务栏往上滑调出程序菜单,这个手势直观且便捷。安装了合适的软件后,平板模式的它可以被手持娱乐,看点视频听点音乐,连接上键盘后可以用来敲代码、写文章,拿出笔来就可以手写笔记或者画点简单的画。这台设备已经完全替代了我的iPad Pro 10.5的生态位,并且由于Windows的开放系统,它做我想要做的事做的远比iPad更好。
以前用iPad Pro时,我会用iSH运行一些Linux软件。但是iSH作为一个转换Linux系统调用的模拟器,性能不佳,兼容性拉跨,大多数时候我还是把iPad当SSH终端连到服务器上运行软件。作为一台完整的Windows设备,在Surface Pro X上,我可以使用“真正”的Linux。虽然目前似乎无法直接安装Linux系统,但是可以使用WSL 2。我也期望Windows On ARM上的WSL加上WSLg还可以补足Windows ARM64生态不完善的问题,让ARM Windows用上更多“原生”软件。

WSL安装

根据微软的文档,ARM64设备上的WSL 2只能在Windows 10 2004或更高版本上运行,不过目前我手上的设备已经加入了Windows预览体验计划Beta通道,运行着Windows 11 22H2,肯定是满足要求的,假如你的设备还在运行旧系统的话,就可能需要注意一下了。
在2004版本之后的Windows上安装WSL很简单,只需要在有管理员权限的Shell里输入下面的命令,然后重启电脑就好了。
wsl --install
需要注意的是,这个命令默认会安装Ubuntu,如果你想自定义安装的发行版,可以用下面这几条命令。
wsl --install -d 发行版名称 ——安装指定发行版
wsl --list --online ——列出可用发行版
在我的网络环境下,列出可用发行版这一操作多次超时。当发行版列表终于加载出来后,我发现ARM64上的WSL发行版选择比较有限,只有Ubuntu。考虑到ARM Linux的社区建设情况,可选择发行版数量少也在情理之中。
于是,就只能安装Ubuntu了。
输入安装命令后,等待进度条跑完,会提示你进行重启。
重启后,终端会自动启动,并完成剩余的安装工作。之后跟随向导完成首次启动设定,Ubuntu的Shell会自动启动,安装完成。

WSL配置

在WSL安装完成后,我进行了基本的换源设定语言,安装ohmyzsh等操作,让其更好用。
为了统一体验,我还安装了OpenInWSL工具,这是个很好用的工具,可以在Windows资源管理器里直接用WSL中的应用打开文件。你还可以把安装在Windows里的浏览器配置为WSL下默认打开的浏览器

WSL体验

在Surface Pro X上使用WSL 2究竟是一种怎样的体验?答案很简单,就是使用Linux的体验——至少到现在为止,80%吧。将其用于基础的Linux开发是没有问题的,但一旦深入下去,总可能会遇上什么问题。比如说,到目前为止,高通还没给SQ系的GPU可以使用WSLg硬件加速的驱动,WSL上的GUI软件性能并不是很好。如果你使用的是x86的Windows笔记本,你可以通过装Linux双系统或者干脆把Windows系统干掉只用Linux来解决问题,但我们的Surface Pro X,以及至今为止所有“官方”的WoA设备,是无法做到这一点的。考虑到它是一台无风扇、续航也较长的漂亮电脑,我愿意做这些取舍,并期待未来微软对WSL 2的进一步维护。
总而言之,就至今为止的体验而言,它绝对无法成为一个重度用户的主力设备,但完全可以成为轻度用户的主力设备或重度用户的第二台设备。