Hiddify构建试用的Flutter 环境的搭建

一、安装基础软件

Windows

winget install Microsoft.VisualStudio.2022.Community --override "--add Microsoft.VisualStudio.Workload.NativeDesktop --includeRecommended --passive --norestart"

winget install Git.Git

winget install Kitware.CMake
winget install Ninja-build.Ninja

MacOS

# 安装 Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

brew update
brew install git

二、安装 Flutter SDK

Windows

mkdir %USERPROFILE%\sdk

下载flutter

https://storage.googleapis.com/flutter_infra_release/releases/stable/windows/flutter_windows_3.29.3-stable.zip

解压

Expand-Archive `
  -Path "$env:USERPROFILE\Downloads\flutter_windows_3.29.3-stable.zip" `
  -DestinationPath "$env:USERPROFILE\sdk" `
  -Force

增加环境变量, 电脑右键属性,高级属性,环境变量,用户的环境变量,Path追加下面的路径

添加环境变量 PATH%USERPROFILE%\sdk\flutter\bin

    MacOS

    mkdir -p /Volumes/mindata/Applications/aarch64 && cd /Volumes/mindata/Applications/aarch64
    git clone https://github.com/flutter/flutter.git -b 3.29.3

    flutter/bin 加入 PATH(zsh 为例):

    echo 'export PATH="$PATH:/Volumes/mindata/Applications/aarch64/flutter/bin"' >> ~/.zshrc
    source ~/.zshrc

    第一次用可能被 Gatekeeper 隔离,遇到权限或打不开时可去掉隔离:

    xattr -dr com.apple.quarantine /Volumes/mindata/Applications/aarch64/flutter

    三、预拉取依赖并体检

    Windows

    flutter config --enable-windows-desktop
    flutter precache --windows
    flutter doctor -v
    
    make windows-prepare
    flutter precache --windows
    
    如果有目录libcorebin文件夹,需要改成libcore/bin

    MacOS

    flutter precache --ios --android
    flutter doctor
    flutter doctor -v   # 如果排查问题,用详细输出
    
    make macos-prepare
    make ios-prepare
    make android-prepare

    四、创建iOS 开发环境

    MacOS

    从 App Store 安装 Xcode,首次打开让它完成组件安装

    接受许可并设置命令行工具:

    sudo xcodebuild -license accept
    sudo xcode-select -s /Applications/Xcode.app/Contents/Developer

    安装 CocoaPods(Apple Silicon 推荐用 brew 安装):

    brew install cocoapods
    pod setup

    打开 Xcode → Preferences → Locations → Command Line Tools 选择对应版本

    iOS 模拟器:open -a Simulator

    五、Android 开发环境(可选)

    安装 Android Studio

    打开 Android Studio → SDK Manager 勾选:

    • Android SDK Platform(推荐最新稳定版本)
    • Android SDK Platform-Tools
    • Android SDK Build-Tools
    • 需要的系统镜像(Arm64 for Apple Silicon)

    安装 JDK 17(AGP 当前广泛兼容)

    配置环境变量(zsh):

    echo 'export ANDROID_SDK_ROOT="/Volumes/mindata/Applications/Android/sdk"' >> ~/.zshrc
    echo 'export PATH="$PATH:$ANDROID_SDK_ROOT/platform-tools:$ANDROID_SDK_ROOT/tools/bin"' >> ~/.zshrc
    source ~/.zshrc

    让 Flutter 识别 Android Studio(如需要)

    flutter config --android-studio-dir="/Volumes/mindata/Applications/aarch64/Android Studio.app/Contents"
    flutter doctor

    六、Windows开发环境

    安装make

    iwr -useb get.scoop.sh | iex
    scoop install make cmake
    flutter clean
    flutter pub get

    八、快速验证

    flutter create hello_app
    cd hello_app
    flutter run -d ios         # 跑 iOS 模拟器
    # 或
    flutter run -d android     # 跑 Android 模拟器/真机
    发表在 dart | Hiddify构建试用的Flutter 环境的搭建已关闭评论

    Qt VPN Client 完整项目设置指南

    项目目录结构

    QtVPNClient/
    ├── QtVPNClient.pro          # qmake项目文件
    ├── CMakeLists.txt           # cmake项目文件
    ├── main.cpp                 # 程序入口
    ├── mainwindow.h             # 主窗口头文件
    ├── mainwindow.cpp           # 主窗口实现
    ├── mainwindow.ui            # 主窗口UI文件
    ├── vpnmanager.h             # VPN管理器头文件
    ├── vpnmanager.cpp           # VPN管理器实现
    ├── configparser.h           # 配置解析器头文件
    ├── configparser.cpp         # 配置解析器实现
    ├── resources.qrc            # 资源文件
    ├── icons/                   # 图标目录
    │   ├── app_icon.png
    │   ├── connected.png
    │   ├── disconnected.png
    │   └── connecting.png
    ├── platforms/               # 平台特定文件
    │   ├── android/
    │   │   ├── AndroidManifest.xml
    │   │   ├── build.gradle
    │   │   └── res/
    │   ├── ios/
    │   │   ├── Info.plist
    │   │   └── Icons/
    │   ├── windows/
    │   │   ├── app.rc
    │   │   └── app.ico
    │   ├── macos/
    │   │   └── Info.plist
    │   └── linux/
    │       └── qtxrayvpn.desktop
    ├── translations/            # 翻译文件
    │   ├── qtxrayvpn_zh_CN.ts
    │   ├── qtxrayvpn_en_US.ts
    │   └── qtxrayvpn_ja_JP.ts
    ├── scripts/                 # 构建脚本
    │   ├── build.sh
    │   ├── build.bat
    │   ├── deploy_android.sh
    │   ├── deploy_ios.sh
    │   └── install_deps.sh
    ├── docs/                    # 文档
    │   ├── README.md
    │   ├── BUILD.md
    │   └── API.md
    └── third_party/             # 第三方库
        └── xray/
            ├── windows/
            ├── linux/
            ├── macos/
            ├── android/
            └── ios/
    

    1. QtVPNClient.pro (qmake项目文件)

    QT += core widgets network
    
    CONFIG += c++17
    
    TARGET = QtVPNClient
    TEMPLATE = app
    
    # 版本信息
    VERSION = 1.0.0
    QMAKE_TARGET_COMPANY = "VPN Client Team"
    QMAKE_TARGET_PRODUCT = "Qt VPN Client"
    QMAKE_TARGET_DESCRIPTION = "Cross-platform VPN client based on Qt and Xray"
    QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2025"
    
    # 源文件
    SOURCES += \
        main.cpp \
        mainwindow.cpp \
        vpnmanager.cpp \
        configparser.cpp
    
    HEADERS += \
        mainwindow.h \
        vpnmanager.h \
        configparser.h
    
    FORMS += \
        mainwindow.ui
    
    RESOURCES += \
        resources.qrc
    
    # 翻译文件
    TRANSLATIONS += \
        translations/qtxrayvpn_zh_CN.ts \
        translations/qtxrayvpn_en_US.ts \
        translations/qtxrayvpn_ja_JP.ts
    
    # 平台特定配置
    win32 {
        RC_FILE = platforms/windows/app.rc
        LIBS += -lws2_32 -lwininet
    }
    
    macx {
        ICON = icons/app_icon.icns
        QMAKE_INFO_PLIST = platforms/macos/Info.plist
        QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.15
    }
    
    unix:!macx {
        TARGET = qtxrayvpn
        target.path = /usr/local/bin
        desktop.files = platforms/linux/qtxrayvpn.desktop
        desktop.path = /usr/share/applications
        icons.files = icons/app_icon.png
        icons.path = /usr/share/pixmaps
        INSTALLS += target desktop icons
    }
    
    android {
        QT += androidextras
        ANDROID_PACKAGE_SOURCE_DIR = $$PWD/platforms/android
        ANDROID_MIN_SDK_VERSION = 23
        ANDROID_TARGET_SDK_VERSION = 30
    }
    
    ios {
        QMAKE_INFO_PLIST = platforms/ios/Info.plist
        QMAKE_IOS_DEPLOYMENT_TARGET = 12.0
        app_launch_images.files = platforms/ios/Icons/Default*.png
        QMAKE_BUNDLE_DATA += app_launch_images
    }
    
    # 调试/发布配置
    CONFIG(debug, debug|release) {
        DEFINES += DEBUG_MODE
        DESTDIR = debug
    } else {
        DEFINES += RELEASE_MODE
        DESTDIR = release
    }
    
    # 包含路径
    INCLUDEPATH += \
        third_party/xray/include
    
    # 库链接
    LIBS += -L$$PWD/third_party/xray/lib
    

    2. 详细的源代码文件

    main.cpp

    #include <QApplication>
    #include <QStyleFactory>
    #include <QDir>
    #include <QStandardPaths>
    #include <QTranslator>
    #include <QLocale>
    #include <QLibraryInfo>
    #include <QMessageBox>
    #include "mainwindow.h"
    
    #ifdef Q_OS_ANDROID
    #include <QtAndroid>
    #include <QAndroidJniObject>
    #endif
    
    #ifdef Q_OS_WIN
    #include <windows.h>
    #include <io.h>
    #include <fcntl.h>
    #endif
    
    int main(int argc, char *argv[])
    {
        // 启用高DPI支持
        QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
        
        QApplication app(argc, argv);
        
        // 应用程序信息
        app.setApplicationName("Qt VPN Client");
        app.setApplicationDisplayName("Qt VPN Client");
        app.setApplicationVersion("1.0.0");
        app.setOrganizationName("VPN Client Team");
        app.setOrganizationDomain("vpnclient.com");
        
        // 设置应用程序图标
        app.setWindowIcon(QIcon(":/icons/app_icon.png"));
        
    #ifdef Q_OS_WIN
        // Windows控制台支持
        if (AllocConsole()) {
            freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
            freopen_s((FILE**)stderr, "CONOUT$", "w", stderr);
            freopen_s((FILE**)stdin, "CONIN$", "r", stdin);
        }
    #endif
        
        // 国际化支持
        QTranslator translator;
        QTranslator qtTranslator;
        
        QString locale = QLocale::system().name();
        QString translationPath = ":/translations";
        
        if (translator.load(QString("qtxrayvpn_%1").arg(locale), translationPath)) {
            app.installTranslator(&translator);
        }
        
        if (qtTranslator.load(QString("qt_%1").arg(locale), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
            app.installTranslator(&qtTranslator);
        }
        
        // 设置样式
        app.setStyle(QStyleFactory::create("Fusion"));
        
        // 应用样式表
        QString styleSheet = R"(
            QMainWindow {
                background-color: #f0f0f0;
            }
            
            QGroupBox {
                font-weight: bold;
                border: 2px solid #cccccc;
                border-radius: 5px;
                margin-top: 10px;
                padding-top: 10px;
            }
            
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 10px 0 10px;
            }
            
            QPushButton {
                background-color: #0078d4;
                color: white;
                border: none;
                padding: 8px 16px;
                border-radius: 4px;
                font-weight: bold;
            }
            
            QPushButton:hover {
                background-color: #106ebe;
            }
            
            QPushButton:pressed {
                background-color: #005a9e;
            }
            
            QPushButton:disabled {
                background-color: #cccccc;
                color: #666666;
            }
            
            QLineEdit, QSpinBox, QComboBox {
                padding: 6px;
                border: 1px solid #cccccc;
                border-radius: 3px;
                background-color: white;
            }
            
            QLineEdit:focus, QSpinBox:focus, QComboBox:focus {
                border-color: #0078d4;
            }
            
            QTextEdit {
                border: 1px solid #cccccc;
                border-radius: 3px;
                background-color: white;
                font-family: 'Consolas', 'Monaco', monospace;
            }
            
            QProgressBar {
                border: 1px solid #cccccc;
                border-radius: 3px;
                text-align: center;
            }
            
            QProgressBar::chunk {
                background-color: #0078d4;
                border-radius: 2px;
            }
        )";
        app.setStyleSheet(styleSheet);
        
        // 创建配置目录
        QString configDir = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
        QDir().mkpath(configDir);
        
    #ifdef Q_OS_ANDROID
        // Android权限请求
        QStringList permissions;
        permissions << "android.permission.INTERNET"
                    << "android.permission.ACCESS_NETWORK_STATE"
                    << "android.permission.WRITE_EXTERNAL_STORAGE"
                    << "android.permission.READ_EXTERNAL_STORAGE";
        
        for (const QString &permission : permissions) {
            if (QtAndroid::checkPermission(permission) != QtAndroid::PermissionResult::Granted) {
                auto result = QtAndroid::requestPermissionsSync(QStringList() << permission);
                if (result[permission] != QtAndroid::PermissionResult::Granted) {
                    QMessageBox::warning(nullptr, "权限错误", 
                        QString("需要权限: %1").arg(permission));
                }
            }
        }
    #endif
        
        // 检查是否已有实例运行
        QString lockFilePath = configDir + "/app.lock";
        QFile lockFile(lockFilePath);
        if (lockFile.exists()) {
            QMessageBox::information(nullptr, "提示", "应用程序已在运行中");
            return 0;
        }
        
        // 创建锁文件
        if (lockFile.open(QIODevice::WriteOnly)) {
            lockFile.write(QString::number(QApplication::applicationPid()).toUtf8());
            lockFile.close();
        }
        
        // 创建主窗口
        MainWindow window;
        window.show();
        
        int result = app.exec();
        
        // 清理锁文件
        lockFile.remove();
        
        return result;
    }
    

    mainwindow.ui

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>MainWindow</class>
     <widget class="QMainWindow" name="MainWindow">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>800</width>
        <height>650</height>
       </rect>
      </property>
      <property name="minimumSize">
       <size>
        <width>600</width>
        <height>500</height>
       </size>
      </property>
      <property name="windowTitle">
       <string>Qt VPN Client</string>
      </property>
      <property name="windowIcon">
       <iconset resource="resources.qrc">
        <normaloff>:/icons/app_icon.png</normaloff>:/icons/app_icon.png</iconset>
      </property>
      <widget class="QWidget" name="centralwidget">
       <layout class="QVBoxLayout" name="verticalLayout">
        <property name="spacing">
         <number>10</number>
        </property>
        <property name="leftMargin">
         <number>10</number>
        </property>
        <property name="topMargin">
         <number>10</number>
        </property>
        <property name="rightMargin">
         <number>10</number>
        </property>
        <property name="bottomMargin">
         <number>10</number>
        </property>
       </layout>
      </widget>
      <widget class="QMenuBar" name="menubar">
       <property name="geometry">
        <rect>
         <x>0</x>
         <y>0</y>
         <width>800</width>
         <height>22</height>
        </rect>
       </property>
       <widget class="QMenu" name="menuFile">
        <property name="title">
         <string>文件(&amp;F)</string>
        </property>
        <addaction name="actionImportConfig"/>
        <addaction name="actionExportConfig"/>
        <addaction name="separator"/>
        <addaction name="actionExit"/>
       </widget>
       <widget class="QMenu" name="menuHelp">
        <property name="title">
         <string>帮助(&amp;H)</string>
        </property>
        <addaction name="actionAbout"/>
        <addaction name="actionAboutQt"/>
       </widget>
       <widget class="QMenu" name="menuView">
        <property name="title">
         <string>视图(&amp;V)</string>
        </property>
        <addaction name="actionShowLog"/>
        <addaction name="actionAlwaysOnTop"/>
       </widget>
       <addaction name="menuFile"/>
       <addaction name="menuView"/>
       <addaction name="menuHelp"/>
      </widget>
      <widget class="QStatusBar" name="statusbar"/>
      <action name="actionImportConfig">
       <property name="text">
        <string>导入配置(&amp;I)</string>
       </property>
       <property name="shortcut">
        <string>Ctrl+I</string>
       </property>
      </action>
      <action name="actionExportConfig">
       <property name="text">
        <string>导出配置(&amp;E)</string>
       </property>
       <property name="shortcut">
        <string>Ctrl+E</string>
       </property>
      </action>
      <action name="actionExit">
       <property name="text">
        <string>退出(&amp;X)</string>
       </property>
       <property name="shortcut">
        <string>Ctrl+Q</string>
       </property>
      </action>
      <action name="actionAbout">
       <property name="text">
        <string>关于(&amp;A)</string>
       </property>
      </action>
      <action name="actionAboutQt">
       <property name="text">
        <string>关于Qt(&amp;Q)</string>
       </property>
      </action>
      <action name="actionShowLog">
       <property name="text">
        <string>显示日志(&amp;L)</string>
       </property>
       <property name="checkable">
        <bool>true</bool>
       </property>
       <property name="checked">
        <bool>true</bool>
       </property>
      </action>
      <action name="actionAlwaysOnTop">
       <property name="text">
        <string>置顶窗口(&amp;T)</string>
       </property>
       <property name="checkable">
        <bool>true</bool>
       </property>
      </action>
     </widget>
     <resources>
      <include location="resources.qrc"/>
     </resources>
     <connections/>
    </ui>
    

    3. 平台特定文件

    platforms/android/build.gradle

    android {
        compileSdkVersion 30
        buildToolsVersion "30.0.3"
    
        defaultConfig {
            applicationId "com.vpnclient.qt"
            minSdkVersion 23
            targetSdkVersion 30
            versionCode 1
            versionName "1.0.0"
        }
    
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        implementation 'androidx.appcompat:appcompat:1.3.1'
    }
    

    platforms/windows/app.rc

    #include <windows.h>
    
    IDI_ICON1 ICON DISCARDABLE "app.ico"
    
    VS_VERSION_INFO VERSIONINFO
    FILEVERSION 1,0,0,0
    PRODUCTVERSION 1,0,0,0
    FILEFLAGSMASK 0x3fL
    FILEFLAGS 0x0L
    FILEOS 0x40004L
    FILETYPE 0x1L
    FILESUBTYPE 0x0L
    BEGIN
        BLOCK "StringFileInfo"
        BEGIN
            BLOCK "040904b0"
            BEGIN
                VALUE "CompanyName", "VPN Client Team"
                VALUE "FileDescription", "Qt VPN Client"
                VALUE "FileVersion", "1.0.0.0"
                VALUE "LegalCopyright", "Copyright (C) 2025"
                VALUE "OriginalFilename", "QtVPNClient.exe"
                VALUE "ProductName", "Qt VPN Client"
                VALUE "ProductVersion", "1.0.0.0"
            END
        END
        BLOCK "VarFileInfo"
        BEGIN
            VALUE "Translation", 0x409, 1200
        END
    END
    

    platforms/linux/qtxrayvpn.desktop

    [Desktop Entry]
    Version=1.0
    Type=Application
    Name=Qt VPN Client
    Comment=Cross-platform VPN client based on Qt and Xray
    Icon=qtxrayvpn
    Exec=qtxrayvpn
    Categories=Network;
    StartupNotify=true
    StartupWMClass=QtVPNClient
    

    4. 构建脚本

    scripts/build.sh

    #!/bin/bash
    
    set -e
    
    # 颜色定义
    RED='\033[0;31m'
    GREEN='\033[0;32m'
    YELLOW='\033[1;33m'
    BLUE='\033[0;34m'
    NC='\033[0m' # No Color
    
    echo -e "${BLUE}Qt VPN Client Build Script${NC}"
    echo "==============================="
    
    # 检测操作系统
    OS="Unknown"
    case "$(uname -s)" in
        Linux*)     OS=Linux;;
        Darwin*)    OS=Mac;;
        CYGWIN*)    OS=Cygwin;;
        MINGW*)     OS=MinGw;;
        *)          OS="UNKNOWN:$(uname -s)"
    esac
    
    echo -e "${YELLOW}检测到操作系统: $OS${NC}"
    
    # 设置Qt路径
    if [ -z "$Qt6_DIR" ]; then
        case $OS in
            Linux)
                Qt6_DIR="/opt/Qt/6.5.0/gcc_64"
                ;;
            Mac)
                Qt6_DIR="/usr/local/Qt-6.5.0"
                ;;
            *)
                echo -e "${RED}错误: 请设置 Qt6_DIR 环境变量${NC}"
                exit 1
                ;;
        esac
    fi
    
    if [ ! -d "$Qt6_DIR" ]; then
        echo -e "${RED}错误: Qt目录不存在: $Qt6_DIR${NC}"
        exit 1
    fi
    
    export PATH="$Qt6_DIR/bin:$PATH"
    echo -e "${GREEN}Qt路径: $Qt6_DIR${NC}"
    
    # 检查必要工具
    check_tool() {
        if ! command -v $1 &> /dev/null; then
            echo -e "${RED}错误: $1 未找到,请先安装${NC}"
            exit 1
        fi
    }
    
    check_tool cmake
    check_tool make
    
    # 清理旧的构建
    if [ -d "build" ]; then
        echo -e "${YELLOW}清理旧的构建目录...${NC}"
        rm -rf build
    fi
    
    # 创建构建目录
    mkdir -p build
    cd build
    
    echo -e "${BLUE}配置CMake...${NC}"
    cmake .. \
        -DCMAKE_BUILD_TYPE=Release \
        -DQt6_DIR="$Qt6_DIR" \
        -DCMAKE_PREFIX_PATH="$Qt6_DIR"
    
    echo -e "${BLUE}开始编译...${NC}"
    make -j$(nproc)
    
    if [ $? -eq 0 ]; then
        echo -e "${GREEN}构建成功!${NC}"
        echo -e "${GREEN}可执行文件位置: $(pwd)/QtVPNClient${NC}"
    else
        echo -e "${RED}构建失败!${NC}"
        exit 1
    fi
    
    # 创建发布包
    echo -e "${BLUE}创建发布包...${NC}"
    if [ "$OS" = "Linux" ]; then
        "$Qt6_DIR/bin/linuxdeployqt" QtVPNClient -appimage
    elif [ "$OS" = "Mac" ]; then
        "$Qt6_DIR/bin/macdeployqt" QtVPNClient.app -dmg
    fi
    
    echo -e "${GREEN}构建完成!${NC}"
    

    scripts/build.bat

    @echo off
    setlocal enabledelayedexpansion
    
    echo Qt VPN Client Build Script
    echo ===============================
    
    REM 设置Qt路径
    if "%Qt6_DIR%"=="" (
        set Qt6_DIR=C:\Qt\6.5.0\msvc2019_64
    )
    
    if not exist "%Qt6_DIR%" (
        echo 错误: Qt目录不存在: %Qt6_DIR%
        pause
        exit /b 1
    )
    
    set PATH=%Qt6_DIR%\bin;%PATH%
    echo Qt路径: %Qt6_DIR%
    
    REM 检查Visual Studio环境
    if "%VCINSTALLDIR%"=="" (
        echo 正在设置Visual Studio环境...
        call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"
    )
    
    REM 清理旧的构建
    if exist build (
        echo 清理旧的构建目录...
        rmdir /s /q build
    )
    
    REM 创建构建目录
    mkdir build
    cd build
    
    echo 配置CMake...
    cmake .. -DCMAKE_BUILD_TYPE=Release -DQt6_DIR=%Qt6_DIR% -G "Visual Studio 16 2019" -A x64
    
    if errorlevel 1 (
        echo CMake配置失败!
        pause
        exit /b 1
    )
    
    echo 开始编译...
    cmake --build . --config Release --parallel
    
    if errorlevel 1 (
        echo 编译失败!
        pause
        exit /b 1
    )
    
    echo 创建发布包...
    %Qt6_DIR%\bin\windeployqt.exe Release\QtVPNClient.exe
    
    echo 构建完成!
    echo 可执行文件位置: %CD%\Release\QtVPNClient.exe
    pause
    

    5. 部署脚本

    scripts/deploy_android.sh

    #!/bin/bash
    
    echo "Android部署脚本"
    echo "=================="
    
    # 检查Android环境
    if [ -z "$ANDROID_SDK_ROOT" ]; then
        echo "错误: 请设置 ANDROID_SDK_ROOT 环境变量"
        exit 1
    fi
    
    if [ -z "$ANDROID_NDK_ROOT" ]; then
        echo "错误: 请设置 ANDROID_NDK_ROOT 环境变量"
        exit 1
    fi
    
    # 设置Qt Android路径
    Qt6_ANDROID_DIR="/opt/Qt/6.5.0/android_arm64_v8a"
    if [ ! -d "$Qt6_ANDROID_DIR" ]; then
        echo "错误: Qt Android目录不存在: $Qt6_ANDROID_DIR"
        exit 1
    fi
    
    export PATH="$Qt6_ANDROID_DIR/bin:$PATH"
    
    # 清理构建
    rm -rf build-android
    mkdir build-android
    cd build-android
    
    # 配置CMake for Android
    cmake .. \
        -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake" \
        -DANDROID_ABI=arm64-v8a \
        -DANDROID_PLATFORM=android-23 \
        -DQt6_DIR="$Qt6_ANDROID_DIR" \
        -DCMAKE_BUILD_TYPE=Release
    
    # 编译
    make -j$(nproc)
    
    # 打包APK
    "$Qt6_ANDROID_DIR/bin/androiddeployqt" \
        --input android-libQtVPNClient.so-deployment-settings.json \
        --output android-build \
        --android-platform android-30 \
        --jdk "$JAVA_HOME" \
        --gradle
    
    echo "Android APK构建完成!"
    echo "APK位置: android-build/build/outputs/apk/debug/android-build-debug.apk"
    

    6. 文档文件

    docs/README.md

    # Qt VPN Client
    
    一个基于Qt和Xray核心的跨平台VPN客户端应用程序。
    
    ## 功能特性
    
    - 🌍 跨平台支持 (Windows, macOS, Linux, iOS, Android)
    - 🔒 VMess协议支持
    - 🎨 现代化Qt用户界面
    - 📊 实时流量监控
    - 🔔 系统托盘集成
    - 📁 配置导入导出
    - 🌐 多语言支持
    - 🔄 自动重连功能
    
    ## 系统要求
    
    ### 桌面平台
    - **Windows**: Windows 10或更高版本
    - **macOS**: macOS 10.15或更高版本  
    - **Linux**: Ubuntu 18.04+, CentOS 7+或等效发行版
    
    ### 移动平台
    - **Android**: Android 6.0 (API 23)或更高版本
    - **iOS**: iOS 12.0或更高版本
    
    ### 开发要求
    - Qt 6.5.0或更高版本
    - CMake 3.16或更高版本
    - C++17兼容编译器
    - Xray核心文件
    
    ## 快速开始
    
    ### 1. 克隆项目
    ```bash
    git clone https://github.com/your-repo/QtVPNClient.git
    cd QtVPNClient
    

    2. 安装依赖

    # Linux
    sudo apt install qt6-base-dev qt6-tools-dev cmake build-essential
    
    # macOS
    brew install qt@6 cmake
    
    # Windows
    # 下载并安装Qt 6.5.0和Visual Studio 2019+
    

    3. 构建项目

    # Linux/macOS
    chmod +x scripts/build.sh
    ./scripts/build.sh
    
    # Windows
    scripts\build.bat
    

    4. 运行应用

    # Linux/macOS
    ./build/QtVPNClient
    
    # Windows
    build\Release\QtVPNClient.exe
    

    配置说明

    VMess配置格式

    {
        "server": "example.com",
        "port": 443,
        "uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "security": "auto",
        "network": "tcp",
        "remarks": "My Server"
    }
    

    支持的导入格式

    • VMess URL (vmess://)
    • JSON配置文件
    • 文本配置

    使用指南

    1. 配置服务器: 在”服务器配置”区域填入服务器信息
    2. 连接VPN: 点击”连接”按钮建立VPN连接
    3. 监控流量: 在”流量统计”区域查看实时数据
    4. 查看日志: 在”连接日志”区域查看连接状态
    5. 系统托盘: 最小化到系统托盘继续运行

    开发指南

    项目结构

    src/
    ├── main.cpp              # 程序入口
    ├── mainwindow.*          # 主窗口实现
    ├── vpnmanager.*          # VPN连接管理
    ├── configparser.*        # 配置解析
    └── ui/                   # UI文件
    

    添加新功能

    1. 在相应的类中添加功能实现
    2. 更新UI文件(如需要)
    3. 添加单元测试
    4. 更新文档

    编码规范

    • 使用Qt编码规范
    • 函数名使用camelCase
    • 类名使用PascalCase
    • 常量使用UPPER_CASE

    故障排除

    常见问题

    Q: 连接失败怎么办? A: 检查服务器配置是否正确,确保网络连接正常,查看连接日志获取详细错误信息。

    Q: 无法启动Xray核心? A: 确保Xray核心文件存在于系统PATH中,或放置在应用程序同目录下。检查文件权限,Linux/macOS需要执行权限。

    Q: Android版本无法连接? A: 确保已授予所有必要权限,包括网络访问权限。某些Android版本可能需要额外配置。

    Q: 流量统计不准确? A: 流量统计基于Xray API,确保API端口(10085)未被占用。

    日志文件位置

    • Windows: %APPDATA%/VPN Client Team/Qt VPN Client/logs/
    • macOS: ~/Library/Application Support/VPN Client Team/Qt VPN Client/logs/
    • Linux: ~/.config/VPN Client Team/Qt VPN Client/logs/
    • Android: /Android/data/com.vpnclient.qt/files/logs/

    贡献指南

    欢迎贡献代码!请遵循以下步骤:

    1. Fork项目
    2. 创建特性分支 (git checkout -b feature/AmazingFeature)
    3. 提交更改 (git commit -m 'Add some AmazingFeature')
    4. 推送到分支 (git push origin feature/AmazingFeature)
    5. 开启Pull Request

    许可证

    本项目基于MIT许可证 – 查看 LICENSE 文件了解详情。

    致谢

    联系方式

    • 项目主页: https://github.com/your-repo/QtVPNClient
    • 问题反馈: https://github.com/your-repo/QtVPNClient/issues
    • 邮箱: support@vpnclient.com
    
    ### docs/BUILD.md
    ```markdown
    # 构建指南
    
    本文档详细说明如何在各个平台上构建Qt VPN Client。
    
    ## 开发环境准备
    
    ### 通用要求
    - Qt 6.5.0或更高版本
    - CMake 3.16+
    - Git
    - C++17编译器
    
    ### Windows
    ```powershell
    # 安装Chocolatey(如果没有)
    Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
    
    # 安装依赖
    choco install cmake git visualstudio2019community
    
    # 下载Qt 6.5.0
    # 访问 https://www.qt.io/download 下载Qt在线安装器
    

    macOS

    # 安装Homebrew
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    
    # 安装依赖
    brew install qt@6 cmake git
    
    # 设置环境变量
    echo 'export PATH="/usr/local/opt/qt@6/bin:$PATH"' >> ~/.zshrc
    source ~/.zshrc
    

    Linux (Ubuntu/Debian)

    # 更新包列表
    sudo apt update
    
    # 安装依赖
    sudo apt install -y \
        qt6-base-dev \
        qt6-tools-dev \
        qt6-l10n-tools \
        cmake \
        build-essential \
        git \
        pkg-config
    
    # CentOS/RHEL
    sudo yum groupinstall -y "Development Tools"
    sudo yum install -y \
        qt6-qtbase-devel \
        qt6-qttools-devel \
        cmake \
        git
    

    获取源码

    git clone https://github.com/your-repo/QtVPNClient.git
    cd QtVPNClient
    git submodule update --init --recursive
    

    平台特定构建

    Windows构建

    使用Visual Studio

    # 设置环境
    call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"
    
    # 设置Qt路径
    set Qt6_DIR=C:\Qt\6.5.0\msvc2019_64
    set PATH=%Qt6_DIR%\bin;%PATH%
    
    # 构建
    mkdir build
    cd build
    cmake .. -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=Release
    cmake --build . --config Release --parallel
    
    # 部署
    %Qt6_DIR%\bin\windeployqt.exe Release\QtVPNClient.exe
    

    使用MinGW

    set Qt6_DIR=C:\Qt\6.5.0\mingw_64
    set PATH=%Qt6_DIR%\bin;C:\Qt\Tools\mingw900_64\bin;%PATH%
    
    mkdir build
    cd build
    cmake .. -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release
    cmake --build . --parallel
    
    %Qt6_DIR%\bin\windeployqt.exe QtVPNClient.exe
    

    macOS构建

    # 设置Qt路径
    export Qt6_DIR="/usr/local/Qt-6.5.0"
    export PATH="$Qt6_DIR/bin:$PATH"
    
    # 构建
    mkdir build
    cd build
    cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15
    make -j$(sysctl -n hw.ncpu)
    
    # 创建应用包
    $Qt6_DIR/bin/macdeployqt QtVPNClient.app
    
    # 创建DMG(可选)
    $Qt6_DIR/bin/macdeployqt QtVPNClient.app -dmg
    

    Linux构建

    # 设置环境
    export Qt6_DIR="/opt/Qt/6.5.0/gcc_64"  # 或系统Qt路径
    export PATH="$Qt6_DIR/bin:$PATH"
    
    # 构建
    mkdir build
    cd build
    cmake .. -DCMAKE_BUILD_TYPE=Release
    make -j$(nproc)
    
    # 创建AppImage(需要linuxdeployqt)
    wget -c -nv "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage"
    chmod a+x linuxdeployqt-continuous-x86_64.AppImage
    ./linuxdeployqt-continuous-x86_64.AppImage QtVPNClient -appimage
    

    Android构建

    # 设置Android环境
    export ANDROID_SDK_ROOT="/path/to/android-sdk"
    export ANDROID_NDK_ROOT="/path/to/android-ndk"
    export JAVA_HOME="/path/to/jdk"
    
    # 设置Qt Android
    export Qt6_ANDROID_DIR="/opt/Qt/6.5.0/android_arm64_v8a"
    export PATH="$Qt6_ANDROID_DIR/bin:$PATH"
    
    # 构建
    mkdir build-android
    cd build-android
    
    cmake .. \
        -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake" \
        -DANDROID_ABI=arm64-v8a \
        -DANDROID_PLATFORM=android-23 \
        -DQt6_DIR="$Qt6_ANDROID_DIR" \
        -DCMAKE_BUILD_TYPE=Release
    
    make -j$(nproc)
    
    # 打包APK
    $Qt6_ANDROID_DIR/bin/androiddeployqt \
        --input android-libQtVPNClient.so-deployment-settings.json \
        --output android-build \
        --android-platform android-30 \
        --gradle
    

    iOS构建

    # 设置Xcode环境
    sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
    
    # 设置Qt iOS
    export Qt6_IOS_DIR="/opt/Qt/6.5.0/ios"
    export PATH="$Qt6_IOS_DIR/bin:$PATH"
    
    # 构建
    mkdir build-ios
    cd build-ios
    
    cmake .. \
        -DCMAKE_SYSTEM_NAME=iOS \
        -DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 \
        -DQt6_DIR="$Qt6_IOS_DIR" \
        -DCMAKE_BUILD_TYPE=Release \
        -DCMAKE_OSX_ARCHITECTURES="arm64"
    
    make -j$(sysctl -n hw.ncpu)
    
    # 在Xcode中打开项目进行签名和部署
    open QtVPNClient.xcodeproj
    

    构建选项

    CMake选项

    # 启用调试模式
    -DCMAKE_BUILD_TYPE=Debug
    
    # 指定安装前缀
    -DCMAKE_INSTALL_PREFIX=/usr/local
    
    # 启用测试
    -DBUILD_TESTING=ON
    
    # 静态链接
    -DBUILD_STATIC=ON
    
    # 自定义Xray路径
    -DXRAY_CORE_PATH=/path/to/xray
    

    qmake构建(替代方案)

    # 生成Makefile
    qmake QtVPNClient.pro CONFIG+=release
    
    # 编译
    make -j$(nproc)  # Linux/macOS
    nmake            # Windows
    

    打包发布

    Windows安装程序

    # 使用NSIS
    makensis installer.nsi
    
    # 使用WiX Toolset
    candle installer.wxs
    light installer.wixobj
    

    macOS PKG

    productbuild --component QtVPNClient.app /Applications QtVPNClient.pkg
    

    Linux包

    DEB包

    # 创建debian目录结构
    mkdir -p debian/DEBIAN
    mkdir -p debian/usr/local/bin
    mkdir -p debian/usr/share/applications
    
    # 复制文件
    cp QtVPNClient debian/usr/local/bin/
    cp platforms/linux/qtxrayvpn.desktop debian/usr/share/applications/
    
    # 创建控制文件
    cat > debian/DEBIAN/control << EOF
    Package: qtxrayvpn
    Version: 1.0.0
    Section: net
    Priority: optional
    Architecture: amd64
    Depends: libc6, libqt6core6, libqt6gui6, libqt6widgets6
    Maintainer: VPN Client Team <support@vpnclient.com>
    Description: Cross-platform VPN client based on Qt and Xray
    EOF
    
    # 构建DEB包
    dpkg-deb --build debian qtxrayvpn_1.0.0_amd64.deb
    

    RPM包

    # 创建spec文件
    cat > qtxrayvpn.spec << EOF
    Name: qtxrayvpn
    Version: 1.0.0
    Release: 1%{?dist}
    Summary: Cross-platform VPN client
    
    License: MIT
    URL: https://github.com/your-repo/QtVPNClient
    Source0: %{name}-%{version}.tar.gz
    
    BuildRequires: qt6-qtbase-devel, cmake, gcc-c++
    Requires: qt6-qtbase, qt6-qttools
    
    %description
    A cross-platform VPN client based on Qt and Xray core.
    
    %prep
    %autosetup
    
    %build
    %cmake
    %cmake_build
    
    %install
    %cmake_install
    
    %files
    %{_bindir}/qtxrayvpn
    %{_datadir}/applications/qtxrayvpn.desktop
    %{_datadir}/pixmaps/qtxrayvpn.png
    
    %changelog
    * $(date "+%a %b %d %Y") VPN Client Team <support@vpnclient.com> - 1.0.0-1
    - Initial release
    EOF
    
    # 构建RPM
    rpmbuild -ba qtxrayvpn.spec
    

    持续集成

    GitHub Actions配置

    # .github/workflows/build.yml
    name: Build
    
    on: [push, pull_request]
    
    jobs:
      build:
        strategy:
          matrix:
            os: [ubuntu-latest, windows-latest, macos-latest]
            
        runs-on: ${{ matrix.os }}
        
        steps:
        - uses: actions/checkout@v3
        
        - name: Install Qt
          uses: jurplel/install-qt-action@v3
          with:
            version: '6.5.0'
            
        - name: Configure CMake
          run: cmake -B build -DCMAKE_BUILD_TYPE=Release
          
        - name: Build
          run: cmake --build build --config Release
          
        - name: Test
          run: ctest --test-dir build -C Release
          
        - name: Package
          run: |
            if [ "${{ runner.os }}" == "Windows" ]; then
              windeployqt build/Release/QtVPNClient.exe
            elif [ "${{ runner.os }}" == "macOS" ]; then
              macdeployqt build/QtVPNClient.app -dmg
            else
              linuxdeployqt build/QtVPNClient -appimage
            fi
    

    故障排除

    常见构建错误

    Qt not found

    # 确保Qt路径正确
    export Qt6_DIR=/path/to/qt6
    export PATH="$Qt6_DIR/bin:$PATH"
    

    CMake version too old

    # 更新CMake
    pip install cmake --upgrade
    

    Missing dependencies

    # 检查pkg-config
    pkg-config --list-all | grep -i qt
    
    # 安装缺失的包
    sudo apt install qt6-*-dev
    

    Android NDK not found

    # 确保NDK路径正确
    export ANDROID_NDK_ROOT=/path/to/ndk
    

    清理构建

    # 完全清理
    rm -rf build build-*
    git clean -xdf
    
    # 重新开始
    mkdir build && cd build
    cmake .. && make
    
    
    ## 7. 翻译文件
    
    ### translations/qtxrayvpn_zh_CN.ts
    ```xml
    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE TS>
    <TS version="2.1" language="zh_CN">
    <context>
        <name>MainWindow</name>
        <message>
            <source>Qt VPN Client</source>
            <translation>Qt VPN客户端</translation>
        </message>
        <message>
            <source>Connect</source>
            <translation>连接</translation>
        </message>
        <message>
            <source>Disconnect</source>
            <translation>断开</translation>
        </message>
        <message>
            <source>Connection Status</source>
            <translation>连接状态</translation>
        </message>
        <message>
            <source>Server Configuration</source>
            <translation>服务器配置</translation>
        </message>
        <message>
            <source>Traffic Statistics</source>
            <translation>流量统计</translation>
        </message>
        <message>
            <source>Connection Log</source>
            <translation>连接日志</translation>
        </message>
        <message>
            <source>Server:</source>
            <translation>服务器:</translation>
        </message>
        <message>
            <source>Port:</source>
            <translation>端口:</translation>
        </message>
        <message>
            <source>UUID:</source>
            <translation>UUID:</translation>
        </message>
        <message>
            <source>Security:</source>
            <translation>加密方式:</translation>
        </message>
        <message>
            <source>Remarks:</source>
            <translation>备注:</translation>
        </message>
        <message>
            <source>Upload:</source>
            <translation>上传:</translation>
        </message>
        <message>
            <source>Download:</source>
            <translation>下载:</translation>
        </message>
        <message>
            <source>Total:</source>
            <translation>总计:</translation>
        </message>
    </context>
    </TS>
    

    8. 完整的资源文件

    resources.qrc

    <RCC>
        <qresource prefix="/">
            <file>icons/app_icon.png</file>
            <file>icons/connected.png</file>
            <file>icons/disconnected.png</file>
            <file>icons/connecting.png</file>
            <file>icons/error.png</file>
            <file>icons/settings.png</file>
            <file>icons/about.png</file>
            <file>icons/exit.png</file>
            <file>icons/import.png</file>
            <file>icons/export.png</file>
            <file>translations/qtxrayvpn_zh_CN.qm</file>
            <file>translations/qtxrayvpn_en_US.qm</file>
            <file>translations/qtxrayvpn_ja_JP.qm</file>
            <file>stylesheets/default.qss</file>
            <file>stylesheets/dark.qss</file>
        </qresource>
    </RCC>
    

    快速搭建指南

    为了快速搭建这个项目,请按以下步骤操作:

    1. 创建项目目录

    mkdir QtVPNClient
    cd QtVPNClient
    

    2. 创建所有必要的文件

    按照上述文档中的内容,创建相应的文件和目录结构。

    3. 准备图标资源

    icons/ 目录下放置所需的PNG图标文件:

    • app_icon.png (128×128)
    • connected.png (24×24)
    • disconnected.png (24×24)
    • connecting.png (24×24)
    • error.png (24×24)

    4. 下载Xray核心

    Xray Releases 下载对应平台的xray可执行文件,放置在 third_party/xray/ 目录下。

    5. 构建项目

    # 使用CMake
    mkdir build && cd build
    cmake .. -DCMAKE_BUILD_TYPE=Release
    make -j$(nproc)
    
    # 或使用qmake
    qmake QtVPNClient.pro CONFIG+=release
    make
    

    这个完整的项目结构提供了:

    • 跨平台支持的CMake和qmake构建系统
    • 完整的Qt应用程序框架
    • 国际化支持
    • 平台特定的配置文件
    • 自动化构建和部署脚本
    • 详细的文档和使用指南

    您可以基于这个框架快速搭建一个功能完整的VPN客户端应用程序。

    发表在 C/C++ | Qt VPN Client 完整项目设置指南已关闭评论

    libXray 编译成动态库

    一、前言

    libXray是指将 Xray-core 编译为一个静态或动态库,通常是为了集成到 Android、iOS 或其他项目中(例如通过 JNI/FFI),那么这是构建 VPN 客户端或代理应用时常见的用途。

    用途示例:

    • Android VPN(与 VpnService 结合使用)
    • iOS/macOS(通过 NEPacketTunnelProvider 调用)
    • Qt 或 Flutter 集成 Xray 配置管理器

    二、移动版本环境搭建

    go环境安装

    从官网下载官方安装包

    ✅ 步骤 1:访问:https://go.dev/dl/

    ✅ 步骤 2:下载对应 macOS 的 .tar.gz压缩包(比如 go1.22.3.darwin-amd64.tar.gz)

    ✅ 步骤 3:解压到你程序的目录,我这里是/Volumes/mindata/Applications/aarch64

    ✅ 步骤 4添加环境变量(编辑 ~/.zshrc~/.bash_profile):

      nano -w ~/.bash_profile 
      export PATH=$PATH:/Volumes/mindata/Applications/aarch64/go/bin
      export GOPATH=$HOME/go

      安装gomobile

      source ~/.bash_profile
      GOBIN=/Volumes/mindata/Applications/aarch64/go/bin go install golang.org/x/mobile/cmd/gomobile@latest
      GOBIN=/Volumes/mindata/Applications/aarch64/go/bin go install golang.org/x/mobile/cmd/gobind@latest
      gomobile init

      下载安卓NDK

      ✅ 步骤 1:安装 Android SDK

      推荐使用 Android Studio 安装 Android SDK,步骤如下:

      1. 下载 Android Studio:https://developer.android.com/studio
      2. 安装并首次启动 Android Studio
      3. 在安装向导或设置中勾选 Android SDK
      4. 记住 Android SDK 的安装路径(一般默认在:~/Library/Android/sdk

      ✅ 步骤 2:设置环境变量(如果路径不是默认)

      如果你安装 SDK 到了非默认路径,比如 /Users/zeno/Android/Sdk,那你需要设置环境变量:

      永久设置(写入 shell 配置):

      nano -w ~/.bash_profile 
      export ANDROID_HOME=/Volumes/mindata/Applications/Android/sdk
      export PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools

      再次初始化 gomobile

      source ~/.bash_profile 
      gomobile init

      三、生成移动版本库

      python3 build/main.py apple go
      LibXray.xcframework
      python3 build/main.py android
      libXray.aar
      libXray-sources.jar
      发表在 go | libXray 编译成动态库已关闭评论

      binlog2sql解析binlog,生成SQL

      一、创建环境

      需要使用python3.11,新版本有兼容问题

      pacman -U python311-3.11.9-2-x86_64.pkg.tar.zst
      /usr/bin/python3.11 -m venv /srv/binlog2sql
      /srv/binlogsql/bin/pip3 install --upgrade pip

      二、安装binlog2sql

      cd /srv
      git clone https://github.com/michael-liumh/binlog2sql.git
      cd binlog2sql && pip3 install -r requirements.txt

      三、生成SQL

      source /srv/binlogsql/bin/activate
      ./binlog2sql.py -h 127.0.0.1 -u root -p admin3579 -d opine --stop-never --table-per-file --start-file binlog.000018

      四、处理生成的SQL

      发表在 Python | binlog2sql解析binlog,生成SQL已关闭评论

      自建archlinux的源

      在 Arch Linux 上自建一个第三方软件源(repo)可以让你集中管理打包好的 .pkg.tar.zst 包,方便多台机器共享、自动升级。以下是完整步骤:

      ✅ 一、自建 Arch Repo 的基本原理

      本质就是:

      你用 repo-add 命令生成一个 .db.tar.zst 文件,然后把 .pkg.tar.zst.db* 文件放到 HTTP/FTP 或本地文件服务器上,其他机器即可通过 pacman 使用这个源。

      ✅ 二、自建步骤(HTTP 服务)

      1️⃣ 准备打包目录

      mkdir -p /srv/http/archlinux/ovsea/os/x86_64
      cp your-package-1.0-1-x86_64.pkg.tar.zst /srv/http/archlinux/ovsea/os/x86_64
      

      2️⃣ 生成数据库文件(.db)

      cd /srv/http/archlinux/ovsea/os/x86_64
      repo-add myrepo.db.tar.gz *.pkg.tar.zst

      结果会生成:

      • myrepo.db.tar.gz(或 .db.tar.zst,随 pacman 版本而变)
      • myrepo.files.tar.gz

      如果你希望 .dbzst 格式(新版默认),可用:

      repo-add --new myrepo.db.tar.zst *.pkg.tar.zst

      3️⃣ 启动 HTTP 服务(推荐用 nginx 或 python)

      server {
          listen 80;
          server_name your_repo_host;
      
          location / {
              autoindex on;
              root //srv/http/archlinux;
          }
      }

      ✅ 三、上传/更新包后

      每次添加新包都要重新运行:

      repo-add myrepo.db.tar.zst new-package.pkg.tar.zst

      删除旧包也建议用:

      repo-remove myrepo.db.tar.zst old-package-name

      发表在 Linux | 自建archlinux的源已关闭评论

      Dubbo指定某一个 Provider 执行指令或请求

      Dubbo 可以实现“只向某个指定 Provider 发送调用”的机制,你可以通过以下几种方式实现指定某一个 Provider 执行指令或请求,这在做远程控制、运维命令下发、灰度测试等场景中非常常见。

      ✅ 方案一:使用 Dubbo 的 parameters + 扩展路由规则

      你可以给某个请求添加自定义参数,让特定 Provider 根据参数自行识别和执行。

      示例:客户端设置指令标识参数

      RpcContext.getContext().setAttachment("target-ip", "192.168.1.10");
      yourService.command("reboot");

      然后在 Provider 中做判断:

      String targetIp = RpcContext.getContext().getAttachment("target-ip");
      String localIp = InetAddress.getLocalHost().getHostAddress();
      
      if (targetIp.equals(localIp)) {
          // 只处理给自己的指令
          doReboot();
      }

      ✅ 适用于多个 Provider 接收但只一个执行场景。

      ✅ 方案二:使用 Dubbo 标签路由(tag routing)

      Dubbo 支持通过“标签”将请求路由到特定 Provider(按 IP、标签等)。

      步骤:

      1. 给 Provider 打标签:

      application.properties 或注解中配置:

      dubbo.provider.tag=provider-A

      或注解方式:

      @DubboService(parameters = {"tag", "provider-A"})

      Consumer 指定 tag:

      RpcContext.getContext().setAttachment("dubbo.tag", "provider-A");
      yourService.command("reboot");

      这样就只会路由到带有 tag = provider-A 的 Provider 上。

      ⚠️ 前提:开启了标签路由支持(Dubbo 2.7+),且该服务支持多个 Provider 实例。

      发表在 Java | Dubbo指定某一个 Provider 执行指令或请求已关闭评论

      libopenconnect 的自定义 IO 支持补丁

      内容包括:

      • openconnect_set_custom_io(...):注册 read/write 回调
      • 修改 setup_tun_device(...)tun_mainloop(...),跳过系统 TUN,走自定义回调逻辑
      • write_packet(...) 内也支持自定义写出

      ✅ 集成方式:

      1. 在 openconnect-internal.h 中添加:

      int openconnect_set_custom_io(struct openconnect_info *vpninfo,
                                    ssize_t (*read_cb)(void *buf, size_t len),
                                    ssize_t (*write_cb)(const void *buf, size_t len));

      2. 修改 setup_tun_device()tun_mainloop()write_packet() 函数(或用 #ifdef IOS_TUN_SUPPORT 包裹)

      // patch_openconnect_custom_io.c
      // 为 libopenconnect 添加自定义 IO 支持(适用于 iOS/macOS 的 TUN 替代方案)
      
      #include "openconnect-internal.h"
      
      // 自定义 read/write 回调
      static ssize_t (*custom_read_cb)(void *buf, size_t len) = NULL;
      static ssize_t (*custom_write_cb)(const void *buf, size_t len) = NULL;
      
      int openconnect_set_custom_io(struct openconnect_info *vpninfo,
                                    ssize_t (*read_cb)(void *buf, size_t len),
                                    ssize_t (*write_cb)(const void *buf, size_t len)) {
          custom_read_cb = read_cb;
          custom_write_cb = write_cb;
          vpninfo->tun_is_custom = 1;
          return 0;
      }
      
      int setup_tun_device(struct openconnect_info *vpninfo) {
          if (vpninfo->tun_is_custom) {
              vpninfo->tun_fd = -1; // 标记为不使用系统 tun
              return 0;
          }
          // 原有平台相关 TUN 初始化保留
          return real_setup_tun_device(vpninfo); // 伪函数,表示原有实现
      }
      
      int tun_mainloop(struct openconnect_info *vpninfo, int *timeout) {
          if (vpninfo->tun_is_custom && custom_read_cb && custom_write_cb) {
              // 示例处理逻辑:
              uint8_t buf[4096];
              ssize_t len = custom_read_cb(buf, sizeof(buf));
              if (len > 0) {
                  return process_received_packet(vpninfo, buf, len); // 解包处理
              }
              return 0;
          }
      
          return real_tun_mainloop(vpninfo, timeout); // 原有逻辑
      }
      
      int write_packet(struct openconnect_info *vpninfo, const void *pkt, size_t len) {
          if (vpninfo->tun_is_custom && custom_write_cb) {
              return custom_write_cb(pkt, len);
          }
          return real_write_packet(vpninfo, pkt, len); // 默认写入
      }

      3. 在 iOS/macOS 的 TunnelProvider.mm 中:

      openconnect_set_custom_io(vpninfo, ios_read_cb, ios_write_cb);

      你只需实现这两个回调函数,桥接 self.packetFlow 的数据收发。

      发表在 C/C++ | libopenconnect 的自定义 IO 支持补丁已关闭评论

      完整的 Qt 6 + CMake 跨平台项目模板

      ✅ 一、目录结构示例

      MyApp/
      ├── CMakeLists.txt
      ├── main.cpp
      ├── mainwindow.cpp
      ├── mainwindow.h
      ├── mainwindow.ui
      └── platform/
          ├── ios/
          └── android/

      ✅ 二、顶层 CMakeLists.txt

      cmake_minimum_required(VERSION 3.16)
      project(MyApp LANGUAGES CXX)
      
      set(CMAKE_CXX_STANDARD 17)
      set(CMAKE_AUTOMOC ON)
      set(CMAKE_AUTORCC ON)
      set(CMAKE_AUTOUIC ON)
      
      # Qt 6 查找
      find_package(Qt6 REQUIRED COMPONENTS Widgets)
      
      add_executable(MyApp
          main.cpp
          mainwindow.cpp
          mainwindow.h
          mainwindow.ui
      )
      
      target_link_libraries(MyApp PRIVATE Qt6::Widgets)
      
      # 平台特定设置
      if(APPLE)
          set_target_properties(MyApp PROPERTIES MACOSX_BUNDLE TRUE)
      elseif(ANDROID)
          set_target_properties(MyApp PROPERTIES ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_SOURCE_DIR}/platform/android")
      endif()

      ✅ 三、主程序 main.cpp

      #include "mainwindow.h"
      #include <QApplication>
      
      int main(int argc, char *argv[]) {
      #if defined(Q_OS_ANDROID)
          QGuiApplication app(argc, argv);
      #else
          QApplication app(argc, argv);
      #endif
          MainWindow w;
          w.show();
          return app.exec();
      }

      ✅ 四、UI 代码(mainwindow.h / mainwindow.cpp

      简单的窗口类,内容略,可参考 Qt Designer。

      ✅ 五、构建和部署平台支持说明

      ✅ macOS / Linux / Windows

      使用 Qt 官方提供的 CMake 支持,直接构建:

      cmake -B build -DCMAKE_PREFIX_PATH=/path/to/Qt6
      cmake --build build

      ✅ Android

      安装 Qt for Android 后:

      cmake -B build_android -DCMAKE_TOOLCHAIN_FILE=$Qt6_ANDROID/armv7/lib/cmake/Qt6/qt.toolchain.cmake
      cmake --build build_android

      ✅ iOS

      确保 Xcode 中安装 Qt6 iOS 工具链:

      cmake -B build_ios -GXcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_PREFIX_PATH=/path/to/Qt6
      cmake --build build_ios --config Release
      

      然后用 Xcode 打开 .xcodeproj 进行签名和部署。

      ✅ 六、平台兼容技巧建议

      技巧建议
      条件编译使用 #ifdef Q_OS_WIN / Q_OS_MAC / Q_OS_ANDROID / Q_OS_IOS 控制平台特有代码
      图片资源使用 Qt 的 qrc 机制统一打包
      输入输出尽量使用 Qt 提供的 QFile、QDir、QStandardPaths 等跨平台封装类
      构建工具尽量统一使用 CMake + Ninja 管理构建

      ✅ 七、Qt 多平台支持说明

      平台支持版本特别说明
      Windows✅ MSVC / MinGW无需额外配置
      macOS✅ Clang可打包为 .app
      Linux✅ GCC建议静态编译或 AppImage
      Android✅ arm64-v8aQt 自带 Gradle 项目支持
      iOS✅ arm64Xcode 打包签名
      发表在 C/C++ | 完整的 Qt 6 + CMake 跨平台项目模板已关闭评论

      Docker内部固定IP,并相互通信

      使用自定义的 bridgemacvlan 网络,Docker 默认的 bridge 网络不支持固定 IP 分配

      1️⃣ 创建自定义 bridge 网络(支持固定 IP)

      docker network create \
        --driver=bridge \
        --subnet=172.16.234.0/24 \
        --gateway=172.16.234.1 \
        navi-net

      2️⃣ 启动容器并指定固定内部 IP(容器内部可见)

      docker run -d \
        --name my-container \
        --network navi-net \
        --ip 172.16.234.2 \
        nginx

      现在容器的 内部 IP 就是 172.16.234.2,你可以在容器内通过命令验证:

      docker exec -it my-container ip addr show eth0

      输出会看到:

      inet 172.16.234.2/24 …

      3️⃣ 同一网络下的其他容器可直接访问

      发表在 Linux | Docker内部固定IP,并相互通信已关闭评论

      PVE 里 virtiofs 设置完整步骤

      virtiofs 是一种现代的文件系统共享机制,主要用于宿主机与虚拟机(尤其是 KVM/QEMU 虚拟机)之间的高效目录共享。相比传统的 9pfs 或 NFS,virtiofs 提供了更好的性能、兼容性和安全性,适合容器、开发环境、嵌入式虚拟化等场景。

      将宿主目录 /hostshare 映射到虚拟机 /mnt/virtshare:

      qemu-system-x86_64 \
        -enable-kvm \
        -m 4096 \
        -cpu host \
        -drive file=vm.qcow2,format=qcow2 \
        -fsdev local,id=myfs,path=/hostshare,security_model=passthrough \
        -device vhost-user-fs-pci,chardev=char0,tag=mytag \
        -chardev socket,id=char0,path=/tmp/vhost.sock \
        ...

      1️⃣ 找到虚拟机的配置文件

      PVE 的每个虚拟机有一个对应的配置文件,路径是:

      /etc/pve/qemu-server/<VMID>.conf

      举例:

      • 如果你的虚拟机 ID 是 100,路径就是:
      /etc/pve/qemu-server/100.conf

      2️⃣ 编辑配置文件

      用编辑器打开:

      nano /etc/pve/qemu-server/100.conf

      添加如下行:

      args: -object memory-backend-memfd,id=mem,size=4G,share=on \
            -numa node,memdev=mem \
            -chardev socket,path=/var/run/qemu-server/100.virtiofs,id=char0 \
            -device vhost-user-fs-pci,chardev=char0,tag=devserver \
            -fsdev local,id=fsdev0,path=/mnt/vmshare/devserver,security_model=passthrough \
      
      virtiofs: data0,mount_tag=devserver,path=/mnt/vmshare/devserver,security_model=passthrough
      参数含义
      virtiofs:开头标记,说明是 virtiofs 类型
      data0设备 ID(随意命名,比如 data0, share0, myshare 都可以)
      mount_tag=devserver客户机里挂载时用的名字,必须记住后面会用到
      path=/mnt/vmshare/devserver宿主机目录,是真实存在的路径(这是你刚才说的路径✅)

      3️⃣如果用的是 PVE Web 页面(图形界面操作)

      PVE 的 Web 界面 数据中心->Directory Mapping, 然后添加名称devserver, 路径/mnt/vmshare/devserver

      进入虚拟机的设置 硬件->virtiofs, 增加,名字devserver即可

      4️⃣在虚拟机里面编辑/etc/fstab

      devserver /mnt/devserver virtiofs defaults 0 0
      发表在 Linux | PVE 里 virtiofs 设置完整步骤已关闭评论