mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-21 20:18:12 +08:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc5e74c6b3 | ||
|
|
e09be46c24 | ||
|
|
4a44b3109e |
@@ -51,7 +51,7 @@ CrossDesk 支持 Windows、macOS、Linux 之间的远程访问,也可从浏览
|
||||
| 实时画面与声音 | H.264 / AV1、30 / 60 fps 设置、硬件编解码选项、远端声音播放;实际能力取决于设备与构建配置 |
|
||||
| 多设备与多显示器 | 近期连接、设备别名、会话标签与远端显示器切换 |
|
||||
| 输入与协作 | 键鼠输入、远端光标同步、组合键、文本剪贴板同步、文件传输 |
|
||||
| 网络与部署 | P2P 直连、TURN 中继、SRTP 加密选项、自托管信令与中继服务 |
|
||||
| 网络与部署 | P2P 直连与默认增强打洞、TURN 中继、默认 SRTP、自托管信令与中继服务 |
|
||||
| Windows 锁屏控制 | 通过 CrossDesk Service 支持锁屏、登录界面及安全桌面的输入转发 |
|
||||
|
||||
各客户端的入口和能力有所不同;桌面会话操作见下文,iOS 的具体功能见 [开发说明](apps/ios/README.md)。
|
||||
@@ -93,7 +93,7 @@ sudo apt install "./crossdesk-linux-amd64-<version>.deb"
|
||||
|
||||
### 从浏览器连接
|
||||
|
||||
1. 保持被控电脑上的 CrossDesk 运行,并确认已连接服务器;在被控端“设置”中保持 **启用 SRTP** 开启。
|
||||
1. 保持被控电脑上的 CrossDesk 运行,并确认已连接服务器;新版本默认请求 SRTP,无需在设置中开启。
|
||||
2. 打开 [Web 客户端](https://web.crossdesk.cn/),输入 **远程设备 ID** 和 **密码**,点击“连接”。
|
||||
3. 连接后使用页面提供的显示器、鼠标模式和键盘控件操作远端;手机和平板也可通过浏览器访问。
|
||||
|
||||
@@ -125,16 +125,19 @@ sudo apt install "./crossdesk-linux-amd64-<version>.deb"
|
||||
点击主窗口右上角 **☰ → 设置**,修改后点击“确认”保存。建议在建立连接前调整;会话进行中部分选项不可修改。设置列表可向下滚动,查看自托管、启动方式与文件保存路径。
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/images/desktop-settings-zh.png" width="720" alt="当前设置界面:画质、帧率、画面偏好、编解码、中继与 SRTP" />
|
||||
<img src="docs/images/desktop-settings-zh.png" width="720" alt="设置界面参考:画质、帧率、画面偏好、编解码与中继;旧图中的 SRTP 开关已移除" />
|
||||
</p>
|
||||
|
||||
SRTP 已默认开启,参考图中的旧 SRTP 开关不再提供。原生桌面默认使用 pool-wide 增强打洞;只有双方支持且认证、映射条件满足时才启动。无 DTLS 指纹旧端仍可兼容连接,但可能回退到未加密 RTP,此类连接不启用增强打洞。
|
||||
|
||||
|
||||
| 设置 | 说明 |
|
||||
| --- | --- |
|
||||
| 画面质量 / 帧率 | 选择低、中、高画质与 30 / 60 fps;实际帧率取决于网络和设备 |
|
||||
| 画面偏好 | 在帧率优先、画质优先、平衡之间选择 |
|
||||
| 视频编码格式 | H.264 / AV1;硬件编解码是否可用取决于平台、硬件与构建选项 |
|
||||
| 启用中继服务 | 允许借助 TURN 中继建立连接;P2P 连接失败时先检查此项 |
|
||||
| 启用 SRTP | 媒体传输加密选项,两端配置需兼容 |
|
||||
| SRTP | 默认请求媒体加密,无本地关闭开关;无指纹旧端保留兼容回退 |
|
||||
| 自托管配置 | 设置服务器地址、信令端口与中继端口,并勾选启用 |
|
||||
| 开机自启 / 启用守护进程 | 调整启动与进程守护行为,按界面提示重启后生效 |
|
||||
| 文件保存路径 | 选择接收文件的目录 |
|
||||
|
||||
+7
-4
@@ -51,7 +51,7 @@ See the [screenshot notes](docs/images/README.md) for image sources and refresh
|
||||
| Live video and audio | H.264 / AV1, 30 / 60 fps settings, hardware codec options, and remote audio; availability depends on hardware and build configuration |
|
||||
| Devices and displays | Recent connections, device aliases, session tabs, and remote display switching |
|
||||
| Input and sharing | Keyboard/mouse input, remote cursor synchronization, shortcuts, text clipboard synchronization, and file transfer |
|
||||
| Networking and deployment | Direct P2P connections, TURN relay, an SRTP encryption option, and self-hosted signaling/relay services |
|
||||
| Networking and deployment | Direct P2P with hole punching enabled by default, TURN relay, SRTP by default, and self-hosted signaling/relay services |
|
||||
| Windows protected desktops | CrossDesk Service forwards input on lock screens, sign-in screens, and secure desktops |
|
||||
|
||||
Controls and capabilities vary by client. Desktop controls are described below; see the [iOS guide](apps/ios/README.md) for native mobile features.
|
||||
@@ -93,7 +93,7 @@ The eye button shows or hides the local password; the pencil button changes it.
|
||||
|
||||
### Connect from a browser
|
||||
|
||||
1. Keep CrossDesk running and connected to the server on the host computer, with **Enable SRTP** enabled in its Settings.
|
||||
1. Keep CrossDesk running and connected to the server on the host computer; current versions request SRTP by default, with no Settings switch.
|
||||
2. Open the [Web Client](https://web.crossdesk.cn/), enter the **Remote Device ID** and **Password**, and click **Connect**.
|
||||
3. Use the page's display, mouse-mode, and keyboard controls during the session. Phone and tablet browsers can also connect.
|
||||
|
||||
@@ -125,16 +125,19 @@ Desktop clients synchronize text clipboard contents. Configure the receiving dir
|
||||
Open **☰ → Settings** in the top-right corner and click **OK** to save. Configure the session before connecting; some settings are disabled during an active session. Scroll down for self-hosting, startup behavior, and the file save path.
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/images/desktop-settings-en.png" width="720" alt="Current settings for quality, frame rate, video preference, codecs, relay, and SRTP" />
|
||||
<img src="docs/images/desktop-settings-en.png" width="720" alt="Settings reference for quality, frame rate, video preference, codecs, and relay; the old SRTP switch has been removed" />
|
||||
</p>
|
||||
|
||||
SRTP is enabled by default; the SRTP switch in this reference image is no longer available. Native desktop builds use pool-wide punching when both peers support it and authentication and mapping conditions are met. Legacy peers without DTLS fingerprints remain compatible through a possible unencrypted RTP fallback, which cannot use enhanced punching.
|
||||
|
||||
|
||||
| Setting | Purpose |
|
||||
| --- | --- |
|
||||
| Video Quality / Video Capture Frame Rate | Low, medium, or high quality and 30 / 60 fps; actual performance depends on the connection and device |
|
||||
| Video Preference | Choose frame-rate priority, quality priority, or balanced adaptation |
|
||||
| Video Encode Format | H.264 / AV1; hardware codec availability depends on the platform, device, and build options |
|
||||
| Enable TURN Service | Allow relay-assisted connections; check this setting if P2P fails |
|
||||
| SRTP | Media encryption option; configurations on both ends must be compatible |
|
||||
| SRTP | Requests media encryption by default, with no local off switch; retains fallback for legacy peers without fingerprints |
|
||||
| Self-Hosted Config | Set the host, signaling port, and TURN port, then enable the checkbox |
|
||||
| Auto Start / Enable Daemon | Configure startup and process supervision; restart as indicated by the UI |
|
||||
| File Save Path | Choose where received files are saved |
|
||||
|
||||
@@ -103,7 +103,12 @@ int ConfigCenter::Load() {
|
||||
ini_.SetLongValue(section_, "turn_mode", static_cast<long>(turn_mode_));
|
||||
persist_config_migration = true;
|
||||
}
|
||||
enable_srtp_ = ini_.GetBoolValue(section_, "enable_srtp", enable_srtp_);
|
||||
// Migrate the former user switch. Keep true in the file for older builds,
|
||||
// while the current application always requests encrypted native media.
|
||||
if (!ini_.GetBoolValue(section_, "enable_srtp", false)) {
|
||||
ini_.SetBoolValue(section_, "enable_srtp", true);
|
||||
persist_config_migration = true;
|
||||
}
|
||||
enable_self_hosted_ =
|
||||
ini_.GetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);
|
||||
|
||||
@@ -165,7 +170,7 @@ int ConfigCenter::Save() {
|
||||
ini_.SetLongValue(section_, "turn_mode", static_cast<long>(turn_mode_));
|
||||
ini_.SetBoolValue(section_, "enable_turn",
|
||||
turn_mode_ != TURN_MODE::DISABLED);
|
||||
ini_.SetBoolValue(section_, "enable_srtp", enable_srtp_);
|
||||
ini_.SetBoolValue(section_, "enable_srtp", true);
|
||||
ini_.SetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);
|
||||
|
||||
// only save when self hosted
|
||||
@@ -301,16 +306,6 @@ int ConfigCenter::SetTurn(bool enable_turn) {
|
||||
return SetTurnMode(turn_mode_);
|
||||
}
|
||||
|
||||
int ConfigCenter::SetSrtp(bool enable_srtp) {
|
||||
enable_srtp_ = enable_srtp;
|
||||
ini_.SetBoolValue(section_, "enable_srtp", enable_srtp_);
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetServerHost(const std::string& signal_server_host) {
|
||||
signal_server_host_ = signal_server_host;
|
||||
ini_.SetValue(section_, "signal_server_host", signal_server_host_.c_str());
|
||||
@@ -472,7 +467,7 @@ bool ConfigCenter::IsEnableTurn() const {
|
||||
return turn_mode_ != TURN_MODE::DISABLED;
|
||||
}
|
||||
|
||||
bool ConfigCenter::IsEnableSrtp() const { return enable_srtp_; }
|
||||
bool ConfigCenter::IsEnableSrtp() const { return true; }
|
||||
|
||||
std::string ConfigCenter::GetSignalServerHost() const {
|
||||
return signal_server_host_;
|
||||
|
||||
@@ -47,7 +47,6 @@ class ConfigCenter {
|
||||
int SetHardwareVideoCodec(bool hardware_video_codec);
|
||||
int SetTurnMode(TURN_MODE turn_mode);
|
||||
int SetTurn(bool enable_turn);
|
||||
int SetSrtp(bool enable_srtp);
|
||||
int SetServerHost(const std::string& signal_server_host);
|
||||
int SetServerPort(int signal_server_port);
|
||||
int SetSelfHosted(bool enable_self_hosted);
|
||||
@@ -99,7 +98,6 @@ class ConfigCenter {
|
||||
VIDEO_ENCODE_FORMAT video_encode_format_ = VIDEO_ENCODE_FORMAT::H264;
|
||||
bool hardware_video_codec_ = false;
|
||||
TURN_MODE turn_mode_ = TURN_MODE::AUTO_UDP_TCP;
|
||||
bool enable_srtp_ = false;
|
||||
std::string signal_server_host_ = "";
|
||||
std::string signal_server_host_default_ = "api.crossdesk.cn";
|
||||
int signal_server_port_ = 0;
|
||||
|
||||
@@ -1230,7 +1230,6 @@ void GuiApplication::ResetSettingsUi() {
|
||||
ui_->main->set_hardware_codec_enabled(hardware_codec_available &&
|
||||
enable_hardware_video_codec_);
|
||||
ui_->main->set_turn_enabled(enable_turn_);
|
||||
ui_->main->set_srtp_enabled(enable_srtp_);
|
||||
ui_->main->set_self_hosted_enabled(enable_self_hosted_);
|
||||
ui_->main->set_autostart_enabled(enable_autostart_);
|
||||
ui_->main->set_daemon_enabled(enable_daemon_);
|
||||
@@ -2419,7 +2418,6 @@ void GuiApplication::SyncStreamWindow() {
|
||||
return;
|
||||
}
|
||||
(*ui_->stream)->set_window_maximized((*ui_->stream)->window().is_maximized());
|
||||
(*ui_->stream)->set_srtp_enabled(enable_srtp_);
|
||||
#if defined(__APPLE__)
|
||||
if (ui_->stream_live_resize_configuration_attempts > 0) {
|
||||
if (ConfigureStreamWindowLiveResize(
|
||||
@@ -2987,7 +2985,6 @@ void GuiApplication::SaveSettingsFromUi() {
|
||||
enable_hardware_video_codec_ = ConfigCenter::IsHardwareVideoCodecAvailable() &&
|
||||
main->get_hardware_codec_enabled();
|
||||
enable_turn_ = main->get_turn_enabled();
|
||||
enable_srtp_ = main->get_srtp_enabled();
|
||||
enable_self_hosted_ = main->get_self_hosted_enabled();
|
||||
enable_autostart_ = main->get_autostart_enabled();
|
||||
enable_daemon_ = main->get_daemon_enabled();
|
||||
@@ -3010,7 +3007,6 @@ void GuiApplication::SaveSettingsFromUi() {
|
||||
video_encode_format_button_value_));
|
||||
config_center_->SetHardwareVideoCodec(enable_hardware_video_codec_);
|
||||
config_center_->SetTurn(enable_turn_);
|
||||
config_center_->SetSrtp(enable_srtp_);
|
||||
config_center_->SetSelfHosted(enable_self_hosted_);
|
||||
config_center_->SetAutostart(enable_autostart_);
|
||||
config_center_->SetDaemon(enable_daemon_);
|
||||
@@ -3056,7 +3052,6 @@ void GuiApplication::SaveSettingsFromUi() {
|
||||
video_encode_format_button_value_last_ = video_encode_format_button_value_;
|
||||
enable_hardware_video_codec_last_ = enable_hardware_video_codec_;
|
||||
enable_turn_last_ = enable_turn_;
|
||||
enable_srtp_last_ = enable_srtp_;
|
||||
enable_self_hosted_last_ = enable_self_hosted_;
|
||||
enable_autostart_last_ = enable_autostart_;
|
||||
enable_daemon_last_ = enable_daemon_;
|
||||
|
||||
@@ -53,8 +53,6 @@
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 218 * dpi_scale_
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_CN 171 * dpi_scale_
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_EN 218 * dpi_scale_
|
||||
#define ENABLE_SRTP_CHECKBOX_PADDING_CN 171 * dpi_scale_
|
||||
#define ENABLE_SRTP_CHECKBOX_PADDING_EN 218 * dpi_scale_
|
||||
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN 171 * dpi_scale_
|
||||
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN 218 * dpi_scale_
|
||||
#define ENABLE_AUTOSTART_PADDING_CN 171 * dpi_scale_
|
||||
|
||||
@@ -57,8 +57,6 @@
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_CN 171
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_SRTP_CHECKBOX_PADDING_CN 171
|
||||
#define ENABLE_SRTP_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN 171
|
||||
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_AUTOSTART_PADDING_CN 171
|
||||
|
||||
@@ -149,7 +149,6 @@ struct TranslationRow {
|
||||
"Enable Hardware Video Codec:", u8"Использовать аппаратный кодек:") \
|
||||
X(enable_turn, u8"启用中继服务:", \
|
||||
"Enable TURN Service:", u8"Включить TURN-сервис:") \
|
||||
X(enable_srtp, u8"启用SRTP:", "Enable SRTP:", u8"Включить SRTP:") \
|
||||
X(self_hosted_server_config, u8"自托管配置", "Self-Hosted Config", \
|
||||
u8"Конфигурация self-hosted") \
|
||||
X(self_hosted_server_settings, u8"自托管设置", "Self-Hosted Settings", \
|
||||
|
||||
@@ -308,7 +308,6 @@ int SettingsManager::Load() {
|
||||
owner_.enable_hardware_video_codec_ =
|
||||
owner_.config_center_->IsHardwareVideoCodec();
|
||||
owner_.enable_turn_ = owner_.config_center_->IsEnableTurn();
|
||||
owner_.enable_srtp_ = owner_.config_center_->IsEnableSrtp();
|
||||
owner_.enable_self_hosted_ = owner_.config_center_->IsSelfHosted();
|
||||
owner_.enable_autostart_ = owner_.config_center_->IsEnableAutostart();
|
||||
owner_.enable_daemon_ = owner_.config_center_->IsEnableDaemon();
|
||||
@@ -335,7 +334,6 @@ int SettingsManager::Load() {
|
||||
owner_.enable_hardware_video_codec_last_ =
|
||||
owner_.enable_hardware_video_codec_;
|
||||
owner_.enable_turn_last_ = owner_.enable_turn_;
|
||||
owner_.enable_srtp_last_ = owner_.enable_srtp_;
|
||||
owner_.enable_self_hosted_last_ = owner_.enable_self_hosted_;
|
||||
owner_.enable_autostart_last_ = owner_.enable_autostart_;
|
||||
|
||||
|
||||
@@ -136,7 +136,6 @@ struct UserSettingsState {
|
||||
int video_encode_format_button_value_ = 0;
|
||||
bool enable_hardware_video_codec_ = true;
|
||||
bool enable_turn_ = true;
|
||||
bool enable_srtp_ = false;
|
||||
char signal_server_ip_[256] = "api.crossdesk.cn";
|
||||
char signal_server_port_[6] = "9099";
|
||||
bool enable_self_hosted_ = false;
|
||||
@@ -147,7 +146,6 @@ struct UserSettingsState {
|
||||
int video_encode_format_button_value_last_ = 0;
|
||||
bool enable_hardware_video_codec_last_ = false;
|
||||
bool enable_turn_last_ = true;
|
||||
bool enable_srtp_last_ = false;
|
||||
bool enable_self_hosted_last_ = false;
|
||||
bool enable_autostart_ = false;
|
||||
bool enable_autostart_last_ = false;
|
||||
|
||||
@@ -48,7 +48,6 @@ export global UiStrings {
|
||||
in-out property <string> codec: "Video codec";
|
||||
in-out property <string> hardware-codec: "Hardware video codec";
|
||||
in-out property <string> turn-relay: "TURN relay";
|
||||
in-out property <string> srtp: "SRTP encryption";
|
||||
in-out property <string> self-hosted: "Self-hosted server";
|
||||
in-out property <string> autostart: "Start with system";
|
||||
in-out property <string> daemon: "Run in background";
|
||||
@@ -247,7 +246,6 @@ export component MainWindow inherits Window {
|
||||
in-out property <int> codec-index: 0;
|
||||
in-out property <bool> hardware-codec-enabled: true;
|
||||
in-out property <bool> turn-enabled: true;
|
||||
in-out property <bool> srtp-enabled: false;
|
||||
in-out property <bool> self-hosted-enabled: false;
|
||||
in-out property <bool> autostart-enabled: false;
|
||||
in-out property <bool> daemon-enabled: false;
|
||||
@@ -1488,11 +1486,6 @@ export component MainWindow inherits Window {
|
||||
Text { x: 8px; y: 3px; width: root.compact-language ? 145px : 212px; height: 24px; text: UiStrings.turn-relay; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; }
|
||||
ImGuiCheckBox { x: root.compact-language ? 157px : 224px; y: 3px; checked <=> root.turn-enabled; enabled: !root.settings-session-active; }
|
||||
}
|
||||
Rectangle {
|
||||
height: 30px;
|
||||
Text { x: 8px; y: 3px; width: root.compact-language ? 145px : 212px; height: 24px; text: UiStrings.srtp; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; }
|
||||
ImGuiCheckBox { x: root.compact-language ? 157px : 224px; y: 3px; checked <=> root.srtp-enabled; enabled: !root.settings-session-active; }
|
||||
}
|
||||
Rectangle {
|
||||
height: 30px;
|
||||
Text { x: 8px; y: 3px; width: root.compact-language ? 145px : 212px; height: 24px; text: UiStrings.privacy-on-connect; color: root.privacy-setting-available ? #202124 : #8b8f95; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; }
|
||||
|
||||
@@ -160,7 +160,6 @@ export component StreamWindow inherits Window {
|
||||
in property <bool> remote-cursor-active: false;
|
||||
in property <int> remote-cursor-shape: 0;
|
||||
in property <bool> audio-enabled: true;
|
||||
in property <bool> srtp-enabled: false;
|
||||
in property <bool> fullscreen-enabled: false;
|
||||
in property <bool> stats-visible: false;
|
||||
in property <[NetworkStatsRow]> stats-rows;
|
||||
@@ -506,20 +505,8 @@ export component StreamWindow inherits Window {
|
||||
width: parent.width - 27px;
|
||||
height: parent.height;
|
||||
background: transparent;
|
||||
if root.srtp-enabled: Text {
|
||||
x: 0px;
|
||||
width: 12px;
|
||||
height: parent.height;
|
||||
text: FontAwesomeIcons.shield-halved;
|
||||
color: #30343b;
|
||||
font-family: "Font Awesome 6 Free";
|
||||
font-weight: 900;
|
||||
font-size: ImGuiFontStyle.base;
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
Text {
|
||||
x: root.srtp-enabled ? 16px : 0px;
|
||||
x: 0px;
|
||||
width: parent.width - self.x;
|
||||
height: parent.height;
|
||||
text: tab.title;
|
||||
|
||||
@@ -69,7 +69,6 @@ inline int ApplyMainWindowStrings(
|
||||
strings.set_hardware_codec(
|
||||
Text(localization::enable_hardware_video_codec[language]));
|
||||
strings.set_turn_relay(Text(localization::enable_turn[language]));
|
||||
strings.set_srtp(Text(localization::enable_srtp[language]));
|
||||
strings.set_self_hosted(
|
||||
Text(localization::self_hosted_server_config[language]));
|
||||
strings.set_autostart(Text(localization::enable_autostart[language]));
|
||||
|
||||
@@ -268,25 +268,6 @@ int GuiApplication::SettingWindow() {
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::enable_srtp[localization_language_index_].c_str());
|
||||
ImGui::SameLine();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 4.275f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 5.955f);
|
||||
}
|
||||
|
||||
ImGui::Checkbox("##enable_srtp", &enable_srtp_);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 1);
|
||||
@@ -569,14 +550,6 @@ int GuiApplication::SettingWindow() {
|
||||
}
|
||||
enable_turn_last_ = enable_turn_;
|
||||
|
||||
// SRTP
|
||||
if (enable_srtp_) {
|
||||
config_center_->SetSrtp(true);
|
||||
} else {
|
||||
config_center_->SetSrtp(false);
|
||||
}
|
||||
enable_srtp_last_ = enable_srtp_;
|
||||
|
||||
if (enable_self_hosted_) {
|
||||
config_center_->SetSelfHosted(true);
|
||||
} else {
|
||||
|
||||
@@ -136,10 +136,7 @@ int GuiApplication::StreamWindow() {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string tab_label =
|
||||
enable_srtp_
|
||||
? std::string(ICON_FA_SHIELD_HALVED) + " " + props->remote_id_
|
||||
: props->remote_id_;
|
||||
const std::string& tab_label = props->remote_id_;
|
||||
if (ImGui::BeginTabItem(tab_label.c_str(), &props->tab_opened_)) {
|
||||
props->tab_selected_ = true;
|
||||
ImGui::SetWindowFontScale(0.6f);
|
||||
@@ -308,4 +305,4 @@ int GuiApplication::StreamWindow() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace crossdesk
|
||||
} // namespace crossdesk
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
#include "config_center.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
using crossdesk::ConfigCenter;
|
||||
|
||||
static void Check(bool result, const char* message) {
|
||||
if (!result) throw std::runtime_error(message);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
try {
|
||||
Check(argc == 2, "provide a new isolated output directory");
|
||||
const std::filesystem::path root(argv[1]);
|
||||
Check(std::filesystem::create_directory(root), "output directory must be new");
|
||||
unsigned cases = 0;
|
||||
for (const auto* setting : {"new", "missing", "false", "true", "invalid"}) {
|
||||
const auto path = root / (std::string(setting) + ".ini");
|
||||
if (std::string(setting) != "new") {
|
||||
std::ofstream file(path);
|
||||
file << "[Settings]\nlanguage=2\nturn_mode=2\nfile_transfer_save_path=/saved-path\n";
|
||||
if (std::string(setting) != "missing") file << "enable_srtp=" << setting << '\n';
|
||||
}
|
||||
ConfigCenter config(path.string());
|
||||
Check(config.IsEnableSrtp(), "fresh and legacy settings must request SRTP");
|
||||
if (std::string(setting) != "new") {
|
||||
Check(config.GetLanguage() == ConfigCenter::LANGUAGE::RUSSIAN &&
|
||||
config.GetTurnMode() == ConfigCenter::TURN_MODE::FORCE_UDP &&
|
||||
config.GetFileTransferSavePath() == "/saved-path",
|
||||
"SRTP migration preserves unrelated preferences");
|
||||
}
|
||||
CSimpleIniA saved;
|
||||
Check(saved.LoadFile(path.string().c_str()) >= 0 &&
|
||||
saved.GetBoolValue("Settings", "enable_srtp", false),
|
||||
"SRTP migration persists before any later settings save");
|
||||
Check(config.Save() == 0 && config.Load() == 0 && config.IsEnableSrtp(),
|
||||
"settings save/reload cannot disable SRTP");
|
||||
ConfigCenter reopened(path.string());
|
||||
Check(reopened.IsEnableSrtp(), "SRTP survives restart");
|
||||
++cases;
|
||||
}
|
||||
std::cout << "PASS SRTP configuration migration cases=" << cases << '\n';
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << "FAIL: " << error.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -339,6 +339,12 @@ function setup_targets()
|
||||
add_includedirs("apps/desktop/src", "apps/desktop/src/config_center",
|
||||
{public = true})
|
||||
|
||||
target("config_center_srtp_test")
|
||||
set_kind("binary")
|
||||
set_default(false)
|
||||
add_deps("config_center")
|
||||
add_files("apps/desktop/tests/config_center_srtp_test.cpp")
|
||||
|
||||
target("assets")
|
||||
set_kind("headeronly")
|
||||
add_includedirs("apps/desktop/src/gui/assets/localization",
|
||||
|
||||
Vendored
+1
-1
Submodule deps/submodules/minirtc updated: 29f5031b42...ce3c41c05c
@@ -1,10 +1,10 @@
|
||||
# CrossDesk 双端 UDP 打洞增强实现方案
|
||||
|
||||
状态:P0 门槛已通过;P1–P4 默认关闭原型、受控运行与跨平台构建已实现;P5 实验工具已交付,真实未知 NAT 对照和最终双端应用切换验收待执行。日期:2026-09-14。第 15 节保留 P0 历史记录,最新进展与边界见第 16 节。
|
||||
状态:P0 门槛已通过,P1–P4 原型、受控运行与跨平台构建已实现。按用户最新要求,原生桌面默认且仅提供 pool-wide,CrossDesk 本机 SRTP 固定开启;无指纹旧端保留原有兼容回退。真实异网已有增强直连证据,默认调整不代表正式收益验收通过。按用户要求,画面、声音、控制和切换质量不纳入本次验收。日期:2026-09-14。第 15 节保留 P0 历史记录,最新进展与边界见第 16 节。
|
||||
|
||||
源码基线:CrossDesk `73e3bf709d7c054df7bd75cfafceda0721c6cda1`;MiniRTC 子模块 `f169a696cb4e6cb59037ec02e85532e30d3c1262`;libnice `0.1.24` 加仓库已有补丁。
|
||||
|
||||
本文规定第一版的范围、接口职责、协议、资源预算、开发顺序和验收条件。第 1–14 节保留设计基线,实际接口及完成情况以第 15–16 节和所链接的执行报告为准。实现和受控测试不代表真实 NAT 成功率已得到验证。
|
||||
本文规定第一版的范围、接口职责、协议、资源预算、开发顺序和验收条件。第 1–14 节以设计基线为基础,第 6 节同步当前实验参数;调整理由、实际接口及完成情况以第 15–16 节和所链接的执行报告为准。实现和受控测试不代表真实 NAT 成功率已得到验证。
|
||||
|
||||
## 1. 目标与取证依据
|
||||
|
||||
@@ -54,7 +54,7 @@ Mac 先前发往 :11968 的短包没有出现在 Windows 捕获中;成功开
|
||||
3. **新 socket 由 libnice 管理。** 不在 GUI、业务数据通道或独立线程中建立脱离 ICE 生命周期的临时 socket 池。
|
||||
4. **探索和 ICE 检查分开调度。** 数百个目的端口属于有限的探索集合;只有少量命中端点进入普通检查表。
|
||||
5. **保留普通 ICE 与已有 30 秒升级窗口。** 增强操作不能重置该窗口、重启 DTLS,或延迟正常的中继可用状态。
|
||||
6. **默认关闭。** 编译扩展存在、运行配置开启、双方声明支持、网络和连接状态满足条件时才启动。
|
||||
6. **默认 pool-wide。** 原生桌面包含扩展、无公开模式选择;双方声明支持、加密认证、网络和连接状态满足条件时才启动。未满足条件仍使用原 ICE/中继。iOS/WebRTC 不启用。
|
||||
|
||||
这会使第一版主要改善“已通过中继连接,但希望随后升为 P2P”的场景。将来需要在没有中继时运行同类算法,应另行设计可验证的信令协调通道。
|
||||
|
||||
@@ -175,6 +175,11 @@ SDP 的能力行不是命令;资源数量由本地配置和认证后的协商
|
||||
|
||||
控制状态消息按 200/400/800 毫秒退避,最多重发 3 次;使用本轮总截止时间约束。每种消息响应幂等,重复 READY/START 不重复分配 socket,也不重启轮次。乱序消息只能在对应状态允许的范围内缓存或拒绝,不能越级启动。
|
||||
|
||||
HELLO 的重发额度耗尽只停止主动发送,在原升级截止内继续接收认证控制消息,
|
||||
允许仍在正常 ICE 重查中的对端随后开始协商。此等待不分配端口池、不刷新快照
|
||||
或重放窗口、不延长任何截止;PREPARE/READY/START 的重发耗尽仍结束协商。
|
||||
取消、过期的同 generation 不能被迟到 HELLO 恢复。原因及复现见 16.4。
|
||||
|
||||
每方向、每密钥域保留 256 项序号重放窗口,允许正常乱序;已处理的控制请求可重新发送缓存响应,不能重新执行副作用。探测 ACK 必须匹配本轮尚有效的 nonce 和本地 socket_id。未认证包不回复。
|
||||
|
||||
上述直接 socket_id 是本地注册表的不透明标识,不是 OS 文件描述符;不得由远端指定本地 FD。
|
||||
@@ -184,14 +189,14 @@ SDP 的能力行不是命令;资源数量由本地配置和认证后的协商
|
||||
### 6.1 anchor 的行为
|
||||
|
||||
1. 保留与稳定公网观测对应的实际 host socket,沿用当前物理接口绑定。
|
||||
2. 在已确认的对端公网 IP 上,从 1024–65535 无放回抽取目的端口,默认 512 个。先生成完整集合再按随机顺序调度,使用可注入的 CSPRNG;测试使用固定种子。
|
||||
2. 在已确认的对端公网 IP 上,从 1024–65535 无放回抽取目的端口,默认 1024 个。先用可注入的 CSPRNG 生成随机集合,再将选中的离散端口按数值递增调度;测试使用固定种子。只对抽样结果排序,避免退化为从最低端口开始的连续扫描。
|
||||
3. 现有普通候选和邻近预测继续由原 ICE 调度;新探索集合避免重复消耗这些目标的探测预算。低于 1024 的探测以及非均匀采样留待后续实验,不声称第一版覆盖 UU 的全部端口范围。
|
||||
4. 每个新目标默认发一次 PROBE。收到 pool 的认证 PROBE 时,从同一 socket 向该包实际来源端点回 PROBE_ACK;该源 IP 必须仍属于本轮批准的对端公网 IP。
|
||||
5. 原始来包地址用于路径发现;不能用包内宣称的地址替代源地址,也不能把 TURN 逻辑地址当成直接源地址。
|
||||
|
||||
### 6.2 pool 的行为
|
||||
|
||||
1. 在同一选定物理接口上分配默认 64 个不同 UDP 本地端口;绑定地址和接口遵循原 ICE socket 的平台规则。
|
||||
1. 在同一选定物理接口上分配默认 128 个不同 UDP 本地端口;绑定地址和接口遵循原 ICE socket 的平台规则。本地端口仍由系统分配,不要求其数值递增,也不据此推断公网映射顺序。
|
||||
2. 新 socket 不为每个端点申请 TURN allocation、启动独立 DTLS 或开展完整多 STUN 采集,只向 anchor 已协商的公网端点探索。
|
||||
3. 以 round-robin 调度 PROBE,最多 3 轮;相邻轮至少间隔 1 秒。全部端点保持可接收至本轮停止或被接入 ICE,不能发完就关闭。
|
||||
4. 收到 anchor 的有效 PROBE 或匹配 ACK 后,对该具体 socket 请求提升并启动普通 ICE 检查;收到 PROBE 时先从同一 socket 回复 ACK。
|
||||
@@ -199,18 +204,18 @@ SDP 的能力行不是命令;资源数量由本地配置和认证后的协商
|
||||
|
||||
两侧时间窗需要重叠,但不要求包在同一毫秒发出。允许丢失第一包、控制消息重传和两端系统时钟有偏差。
|
||||
|
||||
### 6.3 初始配置
|
||||
### 6.3 当前实验配置
|
||||
|
||||
以下是第一轮实验参数,不是从 UU 反推的最佳值。所有值通过经过校验的不可变 `PunchConfig` 进入每个 IceAgent。
|
||||
以下是按用户要求扩大数量并改为离散递增发送后的实验参数,不是从 UU 反推的最佳值。调整理由见第 16.3 节;历史测试仍保留其原始参数。所有值通过经过校验的不可变 `PunchConfig` 进入每个 IceAgent。
|
||||
|
||||
| 参数 | 默认实验值 | v1 硬上限 / 规则 |
|
||||
| --- | --- | --- |
|
||||
| enabled | false | 必须双方开启 |
|
||||
| trigger_delay_ms | 1500 | 从首次选中 relay 计时,并等待 DTLS 指纹已验证 |
|
||||
| mapping_sample_max_age_ms | 15000 | 不跨 generation 或接口变化复用 |
|
||||
| pool_size | 64 | 每会话最多 128;禁止远端扩容越过本地上限 |
|
||||
| target_port_count | 512 | 最多 1024,唯一端口数 |
|
||||
| probe_pps | 100 | 本端全部新增 PROBE/ACK 合计最多 200 包/秒 |
|
||||
| pool_size | 128 | 每会话最多 128;禁止远端扩容越过本地上限 |
|
||||
| target_port_count | 1024 | 最多 1024,唯一端口数 |
|
||||
| probe_pps | 128 | 本端全部新增 PROBE/ACK 合计最多 200 包/秒 |
|
||||
| burst_packets | 8 | token bucket;事件循环滞后时不能瞬间补发整个积压队列 |
|
||||
| pool_probe_rounds | 3 | v1 最多 3;重发也计入总预算 |
|
||||
| round_duration_ms | 10000 | 探索阶段时长,最多 15000,并受原 relay upgrade 截止时间限制 |
|
||||
@@ -227,7 +232,7 @@ SDP 的能力行不是命令;资源数量由本地配置和认证后的协商
|
||||
|
||||
控制包与新增 PROBE/ACK 单独计数;提升后的普通 STUN 检查遵循 libnice 原节拍,但需要记录它们带来的额外事务和重传。媒体包不计入探测预算。
|
||||
|
||||
首次实现用内部配置和明确命名的实验环境变量加载配置,仅读取一次,不增加公开 `Params` 布局或 GUI 开关;例如 `MINIRTC_UDP_PUNCH=off|wide-only|pool-wide`。无效值禁用增强并打印原因。端口池大小等调参入口统一由同一校验函数处理。
|
||||
当前原生桌面配置仅读取一次,固定使用 pool-wide,不增加公开 `Params` 布局或 GUI 模式开关。原 `MINIRTC_UDP_PUNCH` 模式变量不再读取,公开编译开关已移除。端口池大小等诊断调参入口仍由同一校验函数处理;无效预算禁用增强并打印原因,不绕过资源上限。
|
||||
|
||||
`wide-only` 保留同样的协议、角色和锚点选择,但 pool 侧只用一个端点;`pool-wide` 使用配置的端点池。基线 `off` 仍保留已有附近端口预测。不能让实验分组顺带改变其他 ICE、TURN 或重试配置。
|
||||
|
||||
@@ -282,7 +287,7 @@ SDP 的能力行不是命令;资源数量由本地配置和认证后的协商
|
||||
|
||||
### 7.4 候选数量与 end-of-candidates
|
||||
|
||||
探索 socket 在命中之前不参加全量候选配对,不为 64×512 个组合建立检查对象;控制提升数量,逐个接入正常检查。
|
||||
探索 socket 在命中之前不参加全量候选配对,不为端点与目标端口的全部组合建立检查对象;控制提升数量,逐个接入正常检查。
|
||||
|
||||
内部增强端点不通过普通 `new_candidate` 信令发送。为这些端点明确添加内部来源标记,并在现有 `new-candidate-full` 回调处处理,防止在已发送 end-of-candidates 后又误发一批普通候选。既有 host/srflx/relay 的正常收集和结束标记不受影响。
|
||||
|
||||
@@ -357,7 +362,7 @@ INFO 记录阶段和结论;逐包记录放入可选调试日志。所有记录
|
||||
- 满足增强条件并实际启动的连接中的增强成功比例。
|
||||
- 普通 ICE 自行成功、增强成功、继续 relay、连接失败的数量。
|
||||
|
||||
分母包含失败/超时,不把“跳过增强”混入增强失败,也不能只汇总成功样本。记录 P50/P95 建立耗时、额外包数、峰值句柄、CPU 和切换期间的媒体间隙。每条结论都能追到 session ID、二进制版本和抓包。
|
||||
分母包含失败/超时,不把“跳过增强”混入增强失败,也不能只汇总成功样本。记录 P50/P95 建立耗时、额外包数、峰值句柄和 CPU。切换期间的媒体间隙为可选观测,缺测记 null,不作为本次验收门槛。每条结论都能追到 session ID、二进制版本和抓包。
|
||||
|
||||
## 10. 分阶段实施与验收
|
||||
|
||||
@@ -414,7 +419,11 @@ P0 工具只能使用实验提供的明确对端地址,不含广范围探测
|
||||
|
||||
先验收机制和无回归,再评价收益:每个增强成功样本必须能证明直接命中 → 标准 STUN → nomination → 直接媒体;默认关闭路径无行为变化;资源全部在上限内。报告成功比例的不确定性,不从小样本承诺固定提升幅度。
|
||||
|
||||
在真实网络证明净收益且兼容/退出检查通过之前,保持默认关闭;通过后再单独形成默认启用及参数调整变更。
|
||||
验收范围调整(2026-09-14,用户明确要求):不验收画面、声音、控制和切换质量,
|
||||
不再将这些项目列为阻塞项,也不将免验记为测试通过。仍需证明同一命中 socket
|
||||
完成标准 ICE 提名并持续承载应用数据,保留 DTLS、兼容性及资源生命周期检查。
|
||||
|
||||
原计划将收益验收作为默认启用门槛;用户现明确要求默认且仅用 pool-wide,因此调整默认策略。网络适用范围、认证、原截止和资源预算不变;历史 A/B 记录保留,新程序不再提供 A/B 模式,详见 16.5。
|
||||
|
||||
## 11. 测试设计
|
||||
|
||||
@@ -478,7 +487,7 @@ P0 工具只能使用实验提供的明确对端地址,不含广范围探测
|
||||
|
||||
如果 P0 证明某个后端接口或 selected-pair 路径不可按本文实现,应先根据实际 libnice 代码修订这一处设计并给出替代路径,再继续后续阶段;不要把未完成的直接验证改成强制选路或延长超时。
|
||||
|
||||
本方案当前的执行目标是一个默认关闭、可复现实验、完整管理 socket 生命周期的原型。通过 P5 后,才讨论默认启用、扩大网络适用范围,以及首次连接阶段的增强。
|
||||
本方案原始执行目标是默认关闭的实验原型;当前默认策略按用户要求改为 pool-wide,见 16.5。扩大网络适用范围及首次连接阶段增强不属于这次默认调整。
|
||||
|
||||
## 14. 技术依据
|
||||
|
||||
@@ -654,8 +663,8 @@ P0 后端接口、所有权与取消语义按 15.2 冻结,允许进入 P1。P0
|
||||
| P1 | 配置/能力、指纹验证后 exporter、认证协议与协商完成;默认关闭,新旧/未知能力纯逻辑及实际生成能力检查通过 |
|
||||
| P2 | 同源观测、角色、CSPRNG 采样、本地与进程预算完成;首次实现时 Mac/Windows/Linux 各 2,952 个单元断言通过,后续清理回归见验证记录 |
|
||||
| P3 | 独立 ABI 2、64/128 端点、同 socket 提升/标准检查/提名/媒体闭环实现;三个平台受控 runtime 各 12/12 |
|
||||
| P4 | owner context 清理、DRAINING、取消、重连身份和接口/base 检查实现;百次 source/agent 生命周期、Mac/Windows 完整应用、Linux ICE smoke、iOS 静态库已验证。物理网卡真实切换和最终应用互连仍待双端验证 |
|
||||
| P5 | 90 次交错清单与核验工具已准备;有 1 次增强成功双端日志但无该轮物理抓包。后续双端抓包取得 A/off 的 3 个完整窗口样本(均中继),B/wide-only 的 2 次已启动未命中和 1 次启动前跳过。跳过暴露的 READY 重查时序问题已修复,真实双端修复后复测及正式随机 A/B/C 仍待进行;保留先前 SDP 失败,不作收益或默认开启决定 |
|
||||
| P4 | owner context 清理、DRAINING、取消、重连身份和接口/base 检查实现;百次 source/agent 生命周期、Mac/Windows 完整应用、Linux ICE smoke、iOS 静态库已验证。另有新版 Mac→旧 Windows 的非 SRTP 与 DTLS/SRTP 普通中继互通及清理日志;后者本地增强资格满足、旧端不支持,正确不启动增强。物理网卡真实切换和其余旧版连接组合仍待验证 |
|
||||
| P5 | 90 次交错清单与核验工具已准备;完整窗口 A/off 3 次均中继,READY 重查修复后 B/wide-only 3 次均启动但未命中,C/pool-wide 3 次为 2 次增强直连、1 次中继。两个成功会话已补齐 Windows 日志并由仓库 wire 脚本核验 2/2 通过。正式随机 A/B/C、运行版本和捕获统计仍需补齐,不作收益或默认开启决定;画面/声音/控制/切换质量按用户要求免验 |
|
||||
|
||||
### 16.1 对实际源码的必要调整
|
||||
|
||||
@@ -697,10 +706,96 @@ Windows 64 端点百次压力测试在单一窗口到期后拒绝分配;工具
|
||||
各实际收到至少 10 个认证 SRTP 包后才开池。所有初次失败和修正理由保存在报告中,
|
||||
不追溯认定所有历史媒体缺包原因相同。
|
||||
|
||||
用户已报告完成的普通画面/声音/控制/三次重连回归属于 P0 时点,不代表后续原型
|
||||
已通过真实应用切换验收。后续已有异网配对的双端成功日志,下一步补齐物理抓包,
|
||||
并在同步接收端修复后,用同一版本、同一信令
|
||||
与 STUN/TURN 配置,按文档交错执行 A/B/C;首先确认新鲜多 STUN 观测符合一侧稳定、
|
||||
另一侧目的相关。每个增强成功必须核验双端物理报文、标准提名、同一命中 socket、
|
||||
同一 DTLS/SRTP 会话以及实际画面/声音/控制。至少每组 30 次、保留全部失败/缺测。
|
||||
在这些工作完成前,原型仍默认关闭,不能声称已提高真实网络成功率。
|
||||
真实异网 C 组已有两个成功会话完成双端日志、命中 handle、标准提名和物理抓包
|
||||
关联;Windows 的 handle 38/44 分别对应命中本地端口 54058/51630,随后持续直连。
|
||||
成功停止探测后额外 socket 计数各为 1,第三次未命中超时后归零;两端每次均只有
|
||||
一次 DTLS 握手。完整技术证据及限制见所链接的验证记录。
|
||||
|
||||
14:18–14:22 的后续故障实验另记录五个 generation:一轮增强直连、四轮中继。
|
||||
增强直连在约 10.3 秒入站 UDP 中断后沿用命中 socket 恢复,两轮中继在约 29.9/
|
||||
17.2 秒中断后恢复;每次均单次 DTLS,并完成连接清理及后续新 generation 建连。
|
||||
包含 DRAINING 未命中时取消、探测超时及 HELLO 超时;该轮增强成功的仓库 wire
|
||||
核验 1/1 通过。用户补充确认 clumsy 丢包率为 100%;过滤范围、方向、启停时间及
|
||||
真实网卡切换尚未确认,不将观察到的间隙当作精确配置时长或物理接口变化通过,
|
||||
也不并入正式 A/B/C 分母,详见验证记录。
|
||||
|
||||
UU 与 CrossDesk 的端口发现及通道复用差异,按
|
||||
[双端对照测试](p2p-uu-comparison-test-plan.md) 先做 8 次诊断连接。进程重启与保留
|
||||
进程重连分开记录,不把短暂静默当作 NAT 映射清空,也不混入正式 P5 成功率。
|
||||
|
||||
用户在 2026-09-14 明确取消画面、声音、控制和切换质量验收;这些项目免验,
|
||||
不再要求人工回归或质量指标,不追溯标记为已测试。后续用同一版本、同一信令与
|
||||
STUN/TURN 配置交错执行 A/B/C,核对有效观测、双端物理报文、标准提名、同一命中
|
||||
socket、同一 DTLS/SRTP 会话和持续应用数据。至少每组 30 次,保留全部失败/缺测,
|
||||
并完成真实接口变化、重连及连接兼容性检查。在真实收益结论完成前保持默认关闭。
|
||||
|
||||
### 16.3 离散递增与数量调整(2026-09-14,U01/X01 之后)
|
||||
|
||||
按用户要求,将 anchor 的随机无重复目标集合改为按端口数值递增发送,默认目标
|
||||
512 → 1024、pool 64 → 128、每端 PROBE/ACK 速率 100 → 128 PPS。先完成随机
|
||||
抽样再排序,保留原来的全范围抽样分布及候选排除;pool 本地端口继续由系统分配。
|
||||
源码确认 runtime 每 10 ms 最多处理 8 个包,因此 128 PPS 不受“一次回调一个包”
|
||||
限制。单轮无 ACK、无争用的理想调度约 8 秒可完成 1024 个目标;100 PPS 在原
|
||||
10 秒窗口内不能保证覆盖这些目标,因而配套调整速率,没有延长探索或 ICE 截止。
|
||||
|
||||
保留 1024 次 PROBE/ACK 合计预算、8 包突发、200 PPS 进程限速和最多 256 个额外
|
||||
socket。命中、ACK、发送失败、同进程会话争用、原窗口剩余时间不足都可能减少
|
||||
实际新目标数量,1024 是计划上限,不承诺每次发完。双方使用 HELLO 逐项取较小
|
||||
预算,旧参数对端仍收敛到 64/512/100;没有增强能力的旧端保持普通 ICE/中继。
|
||||
本次不改变后端 ABI、仓库 libnice 补丁、选路、命中 socket 接入和 DTLS 生命周期。
|
||||
|
||||
这是一组新的实验配置,历史 U01/X01 结论及参数不改写。需要双方重新构建并重启
|
||||
才能使用完整新参数;旧 P5 manifest 保留显式参数,新批次应重新生成 manifest。
|
||||
数量增加及发送顺序本身不构成真实 NAT 成功率提高的证明。
|
||||
|
||||
本机 Mac 完整构建、5,430 个单元断言、Python 工具 10/10、三种模式兼容性及
|
||||
各 36 个传输层 SDP 组合通过;128 端点 runtime 8/8、DRAINING 2/2 通过。
|
||||
这些是本机回环受控网络结果,Windows 新配置及真实异网尚未运行;详情见
|
||||
[本次回归记录](../deps/submodules/minirtc/tests/ice/lab/punch-runtime-validation-2026-09-14.md#离散递增及扩大默认数量2026-09-14u01x01-之后)。
|
||||
|
||||
|
||||
### 16.4 HELLO 启动错位与映射地址碰撞修复(2026-09-14)
|
||||
|
||||
15:50 批次日志暴露两个边界:一端的合法 ICE 重查使 HELLO 晚于另一端约 2.2 秒
|
||||
的重发结束时间;另一个会话已提升 pool socket,却在 Windows 被归为 ordinary-ice
|
||||
并关闭全部额外 socket。不能把这些现象直接算成探测端口不足或纯日志误判。
|
||||
|
||||
源码与本机最小网络实验确认:旧协商器把 HELLO 重发耗尽当作整体超时;libnice
|
||||
在处理真实 STUN 响应的 XOR-MAPPED-ADDRESS 时,只比较 UDP 传输类型和映射地址,
|
||||
可能复用另一 socket 的候选,从而将命中检查关联到旧 socket。受控复现中两种
|
||||
角色都选错并失去直连传输,和历史日志中的不一致有相同机制;未对历史进程的
|
||||
内存状态作追溯证明。
|
||||
|
||||
修复保留原 HELLO 发送次数,耗尽后被动接收至原截止,由迟到对端的认证 HELLO
|
||||
推进原协议;实际采样新鲜度和剩余时间仍在准备阶段重新检查。libnice 新补丁
|
||||
`punch_socket_identity_0.1.24.patch` 要求普通 UDP 的反射候选同时匹配实际 socket,
|
||||
并让提升候选的反射别名保留内部候选标记。TCP/TURN 的原兼容性判定不变。
|
||||
依赖配方校验补丁 SHA,`punch_v1_revision` 从 2 升至 3,使旧缓存失效;ABI 仍为 2。
|
||||
选路日志增加实际 GSocket 的 bound 和对应 handle,不能以公网四元组代替 socket。
|
||||
|
||||
新增 late-hello 与 mapped-collision 回归,先在旧实现上验证失败,再检验真实
|
||||
TURN → 标准 ICE → 同一命中 socket 上持续 SRTP。默认开关、128/1024/128 PPS
|
||||
实验参数、30 秒升级截止及 DTLS 生命周期不变。结果和 Windows 操作见
|
||||
[修复验证记录](../deps/submodules/minirtc/tests/ice/lab/punch-runtime-validation-2026-09-14.md#hello-启动错位与映射地址碰撞修复2026-09-14)。
|
||||
|
||||
|
||||
### 16.5 默认 pool-wide 与本机 SRTP 固定开启(2026-09-14)
|
||||
|
||||
按用户要求,原生桌面不再提供 off/wide-only/pool-wide 三选一入口,也移除公开
|
||||
构建开关。读取进程配置时直接选择 pool-wide;遗留模式环境变量不再生效,诊断
|
||||
TRACE 与有界预算调参仍保留。内部 Off 是不支持平台/无效参数状态,WideOnly
|
||||
仅保留 v1 协议及 P0/单端点夹具用途,不是产品选项。
|
||||
|
||||
CrossDesk 移除 Slint 和旧 ImGui 设置中的 SRTP 控件、对应 setter 与可变 UI 状态。
|
||||
INI 中旧 false/缺失/无效 SRTP 值迁移为 true,其他配置保留;新建连接始终请求
|
||||
SRTP。用户对旧版兼容提出疑问,因此保留无 DTLS 指纹旧端的原明文回退:这是
|
||||
默认请求加密,不是拒绝所有明文旧端。该回退不能启动增强打洞。原来只根据本地
|
||||
设置显示的加密盾牌被移除,避免在旧端回退时误报已加密。
|
||||
|
||||
没有改变候选/认证门槛、128 个端点、1024 个目标、128 PPS、30 秒总窗口,
|
||||
没有强制选路或重启 DTLS。旧端没有增强能力时继续普通 ICE/中继;iOS 和 WebRTC
|
||||
不启用此增强。默认启用是用户选择,不能从中推导所有 NAT 都能直连。
|
||||
|
||||
实验工具新计划只生成 C/pool-wide,拒绝启动旧 A/B 条目;历史结果仍可读取汇总。
|
||||
同机自动入口、旧端能力与无指纹兼容、SRTP 配置迁移及真实 TURN/DTLS/SRTP
|
||||
验证详见 [本次记录](../deps/submodules/minirtc/tests/ice/lab/punch-runtime-validation-2026-09-14.md#默认-pool-wide-与本机-srtp-固定开启2026-09-14)。
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
# UU 与 CrossDesk 直连差异:双端对照测试
|
||||
|
||||
日期:2026-09-14。状态:U01 和 X01 已收到部分双端证据并完成机制分析;尚未形成
|
||||
符合原始固定条件的完整首次连接/重连对照。X02/U02 待执行。用户确认必须保留 UU
|
||||
管理连接来操作对端,后续按第 9 节调整条件,不要求断开唯一操作通道。
|
||||
|
||||
目标是定位 UU 在当前网络中更容易直连的原因:端口选择、双端发包时序、失败重试,
|
||||
还是重连时保留了可用通道。本轮先做 8 次连接;只检查连接路径与协议证据,画面、
|
||||
声音、控制和切换质量按用户要求免验。它不是正式成功率评估,不替代原方案 P5。
|
||||
|
||||
## 1. 固定条件
|
||||
|
||||
- 使用此前异网组合:Mac 本机与 Windows `10.15.73.245` 所在网络;不要换成同
|
||||
局域网的 Windows。每批保存实际地址、网关、网卡,地址变化后按新环境另记。
|
||||
- 统一由 Mac 控制 Windows;固定软件版本,CrossDesk 使用同一套编译产物和
|
||||
STUN/TURN 配置。UU 使用自己的服务,记录抓包可见的服务器,不假设服务相同。
|
||||
- clumsy 点击 Stop 并退出;本轮不注入丢包、断网或带宽限制。两端关闭代理/TUN/
|
||||
VPN 后保持不变;如不能关闭,标记为另一种网络条件,不混合比较。
|
||||
- 每批只测试一种软件,另一种软件退出;暂停其他远控、下载。Windows 需由本地
|
||||
人员操作,不能依赖即将退出的 UU 会话完成进程重启。这是原始隔离条件;若依赖
|
||||
UU 管理通道操作对端,按第 9 节执行,不要求终止该通道,不声称满足完全隔离。
|
||||
- CrossDesk 双端默认请求 SRTP,运行默认 `pool-wide` 并设置 `TRACE=1`;每批固定实验参数。
|
||||
原 U01/X01 批次为 64 个端点、512 个随机顺序目标、100 PPS;后续按第 10 节
|
||||
使用 128 个端点、1024 个离散递增目标、128 PPS,两批均为 10 秒探索与 3 秒
|
||||
检查收尾。记录实际协商参数,禁止把不同版本与预算混成同一批结果。
|
||||
- 保留系统已有时间设置,记录带时区时间;分析时通过同一报文关联两端,不能直接
|
||||
相减 Mac/Windows 的墙钟得到单程延迟。
|
||||
|
||||
## 2. 批次与“冷启动”的含义
|
||||
|
||||
| 顺序 | 批次 | 软件 | 同一抓包中的两次连接 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | U01 | UU | 进程重启后首次连接 → 保留进程重连 |
|
||||
| 2 | X01 | CrossDesk | 进程重启后首次连接 → 保留进程重连 |
|
||||
| 3 | X02 | CrossDesk | 进程重启后首次连接 → 保留进程重连 |
|
||||
| 4 | U02 | UU | 进程重启后首次连接 → 保留进程重连 |
|
||||
|
||||
这四批合计 8 次连接,每种软件首次连接 2 次、保留进程重连 2 次。顺序反转用于
|
||||
减少先后偏差,数量只够诊断线索;若结果混杂,再追加完整对照块,保留全部失败。
|
||||
本轮用 U/X 命名,不占用原方案 A/off、B/wide-only、C/pool-wide 的分组含义。
|
||||
|
||||
“进程重启”要求两端真正退出相关前台和网络后台进程,重新启动后确认 PID。
|
||||
关闭窗口不一定退出后台。如果 UU 的网络服务仍运行、无法通过正常方式退出,
|
||||
将该批标为“前台重启、后台常驻”,不要当作完全进程重启,不盲目终止未知服务。
|
||||
|
||||
即使进程全部退出并安静 30 秒,也不能保证路由器/运营商 NAT 映射已经过期,
|
||||
因此本轮不声称是“NAT 状态完全清空的冷启动”。不为此重启路由器或改变公网 IP。
|
||||
若首次连接失败,第二次应标为“保留进程重试”,不能宣称复用了已成功的直连。
|
||||
|
||||
## 3. 每批固定操作
|
||||
|
||||
一批一对抓包,覆盖两次连接及中间空闲期,不在断开时停止抓包。
|
||||
|
||||
1. 两端先启动本批抓包;退出相关应用并核对后台/PID/端口,记录 `process_exit`。
|
||||
2. 保持两款软件不发测试流量至少 30 秒,记录 `quiet_end`。若后台仍发包如实记录。
|
||||
3. 只启动本批软件,记录 `app_start`,保存版本、PID、端口快照。
|
||||
4. Mac 点击连接前记录 `first_connect_click`;从点击起观察 60 秒。无论最终为
|
||||
直连、中继、失败或仍连接中,都保留结果,不仅留下成功样本。
|
||||
5. 记录 `first_disconnect` 并断开远控会话,保持软件和后台运行 30 秒;在第
|
||||
15 秒保存端口快照,观察是否仍有原直连 tuple 的保活或数据报。
|
||||
6. 不重启进程,记录 `reconnect_click`,再次连接同一对端并观察 60 秒。
|
||||
7. 记录 `second_disconnect` 并断开,继续抓包 15 秒,保存最后端口快照;随后
|
||||
停止抓包、保存计数与日志,再正常退出软件。
|
||||
|
||||
每次观察期间保持有桌面数据传输;无需人工评价质量。记录应用显示的直连/中继
|
||||
及其变化时间,但最终路径以双端物理抓包核验为准。长时间仍在连接中应单独分类。
|
||||
先完成 U01/X01 四次连接即可检查取证是否齐全,再继续 X02/U02。
|
||||
|
||||
## 4. 抓包命令
|
||||
|
||||
本轮采集 UDP、TCP、ICMP 和 IPv6 ICMP,避免遗漏 UU 可能选择的其他传输路径。
|
||||
不沿用历史公网端口过滤器。两端都抓当前实际联网的物理接口;每批使用新目录。
|
||||
|
||||
### Mac:抓包终端
|
||||
|
||||
当前本机 `en1` 为 `192.168.31.125`;先用 `ifconfig en1` 核对。每批修改 U01
|
||||
为对应批次,复制终端打印的实际目录,避免把后续文件误认为仍在上一次路径。
|
||||
|
||||
```sh
|
||||
umask 077
|
||||
punch_batch=U01
|
||||
punch_run_dir="$(mktemp -d "/private/tmp/crossdesk-uu-${punch_batch}.XXXXXX")"
|
||||
printf '本批目录:%s\n' "$punch_run_dir"
|
||||
sudo tcpdump -i en1 -nn -s 0 -U -Z dijunkun \
|
||||
-w "$punch_run_dir/mac.pcap" \
|
||||
'udp or tcp or icmp or icmp6' 2>&1 | tee -i "$punch_run_dir/mac-capture.log"
|
||||
```
|
||||
|
||||
完成整批后 Ctrl+C;等待 tcpdump 输出 captured/received/dropped 统计并返回
|
||||
提示符。`tee -i` 让 tee 不先被 Ctrl+C 中断,便于保存最终统计;仍应检查日志。
|
||||
|
||||
### Windows:管理员 PowerShell 抓包终端
|
||||
|
||||
```powershell
|
||||
$punchDumpcap = 'C:\Program Files\Wireshark\dumpcap.exe'
|
||||
& $punchDumpcap -D
|
||||
$punchNic = Read-Host '输入上方实际联网的以太网接口编号'
|
||||
$punchBatch = 'U01'
|
||||
$punchRunDir = Join-Path $env:USERPROFILE ("Desktop\CrossDesk-UU-Compare\{0}-{1}" -f $punchBatch, (Get-Date -Format 'yyyyMMdd-HHmmss-fff'))
|
||||
if (Test-Path $punchRunDir) { throw '本批目录已存在,请换新目录' }
|
||||
New-Item -ItemType Directory -Path $punchRunDir -ErrorAction Stop | Out-Null
|
||||
Write-Host "本批目录:$punchRunDir"
|
||||
& $punchDumpcap -i $punchNic -s 0 -B 16 -f 'udp or tcp or icmp or icmp6' -w (Join-Path $punchRunDir 'windows.pcapng') 2> (Join-Path $punchRunDir 'windows-capture.log')
|
||||
```
|
||||
|
||||
另开终端查看 `windows-capture.log` 确认成功启动;完成整批后 Ctrl+C,等待文件
|
||||
关闭。保存 pcapng 接口统计和日志;统计缺失记为缺测,不填零。`-D` 用于列接口,
|
||||
`-i` 选择接口,dumpcap 默认输出 pcapng,详见 [Wireshark 官方说明](https://www.wireshark.org/docs/man-pages/dumpcap.html)。
|
||||
|
||||
## 5. 操作时间、端口和版本
|
||||
|
||||
在各端另开一个记录终端,把目录变量设置为本批抓包终端打印的实际目录。每个
|
||||
关键操作前记录时间,事件名采用第 3 节的名称;如点击晚于记录,应说明大致延迟。
|
||||
|
||||
Mac 示例(将路径替换为真实值,后续事件只改最后一行的事件名):
|
||||
|
||||
```sh
|
||||
punch_run_dir='/private/tmp/替换为本批实际目录'
|
||||
date '+%Y-%m-%dT%H:%M:%S%z first_connect_click' >> "$punch_run_dir/events-mac.txt"
|
||||
```
|
||||
|
||||
在退出后、启动后、首次连接中、断开后第 15 秒、重连中及最终断开后分别执行快照;
|
||||
阶段名依次用 `after_exit`、`after_start`、`first_connected`、`idle_15s`、
|
||||
`reconnected`、`after_disconnect`。替换 `punch_stage`,不要覆盖同名快照:
|
||||
|
||||
```sh
|
||||
punch_stage=after_start
|
||||
date '+%Y-%m-%dT%H:%M:%S%z' > "$punch_run_dir/$punch_stage-time.txt"
|
||||
ps -axo pid,ppid,comm > "$punch_run_dir/$punch_stage-processes.txt"
|
||||
sudo lsof -nP +c 0 -iUDP -iTCP > "$punch_run_dir/$punch_stage-sockets.txt"
|
||||
```
|
||||
|
||||
Windows 记录终端:
|
||||
|
||||
```powershell
|
||||
$punchRunDir = '替换为本批实际目录'
|
||||
"$(Get-Date -Format o) first_connect_click" | Add-Content (Join-Path $punchRunDir 'events-windows.txt')
|
||||
$punchStage = 'after_start'
|
||||
Get-Date -Format o | Set-Content (Join-Path $punchRunDir "$punchStage-time.txt")
|
||||
Get-CimInstance Win32_Process | Select-Object ProcessId, ParentProcessId, Name, ExecutablePath | Export-Csv (Join-Path $punchRunDir "$punchStage-processes.csv") -NoTypeInformation -Encoding UTF8
|
||||
Get-NetUDPEndpoint | Select-Object LocalAddress, LocalPort, OwningProcess, CreationTime | Export-Csv (Join-Path $punchRunDir "$punchStage-udp.csv") -NoTypeInformation -Encoding UTF8
|
||||
Get-NetTCPConnection | Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, State, OwningProcess | Export-Csv (Join-Path $punchRunDir "$punchStage-tcp.csv") -NoTypeInformation -Encoding UTF8
|
||||
```
|
||||
|
||||
UDP 的 `OwningProcess` 用于关联所属 PID,见 [Microsoft 文档](https://learn.microsoft.com/en-us/powershell/module/nettcpip/get-netudpendpoint?view=windowsserver2025-ps)。
|
||||
快照不是无遗漏的 socket 跟踪;相同 PID/端口也不能独立证明同一个 OS socket。
|
||||
结合空闲期持续报文和下一次连接的外层端点,只对实际可见的通道复用作结论。
|
||||
|
||||
每批记录两端应用版本;CrossDesk 保存主仓库及 MiniRTC 提交、EXE SHA-256,UU
|
||||
保存版本和实际网络进程对应可执行文件 SHA-256。Mac 可用 `shasum -a 256 <路径>`,
|
||||
Windows 可用 `Get-FileHash -Algorithm SHA256 <路径>`;进程快照帮助定位后台程序。
|
||||
不要导出命令行、账号密码或完整配置文件。日志和抓包放在私有目录,不提交到 Git。
|
||||
|
||||
CrossDesk 两端启动命令(X01/X02):
|
||||
|
||||
```sh
|
||||
cd /Users/dijunkun/crossdesk
|
||||
MINIRTC_UDP_PUNCH_TRACE=1 xmake r crossdesk
|
||||
```
|
||||
|
||||
```powershell
|
||||
Set-Location E:\SourceCode\crossdesk
|
||||
$env:MINIRTC_UDP_PUNCH_TRACE = '1'
|
||||
.\build\windows\x64\debug\crossdesk.exe
|
||||
```
|
||||
|
||||
如果之前设置过其他 `MINIRTC_UDP_PUNCH_*` 调参变量,先只清除这一前缀的实验
|
||||
变量,再设置模式和 TRACE;两端保持相同默认参数。旧版 Windows 不用于这组对照。
|
||||
运行目录如与示例不同,以本轮已核对的构建位置为准;测试中不重编译或换版本。
|
||||
|
||||
## 6. 交付材料与判定
|
||||
|
||||
每批提交一份 Mac 抓包、一份 Windows 抓包、操作时间和上述快照。X 批次另附
|
||||
两端 MiniRTC 日志;Mac 通常在 `~/Library/Logs/CrossDesk`,Windows 通常在
|
||||
`%LOCALAPPDATA%\CrossDesk\logs`,便携构建也检查程序目录的 logs。按时间匹配,
|
||||
不要把前一次日志拼入本轮;UU 若可正常导出连接诊断日志则一并提供,没有也记录。
|
||||
|
||||
逐次记录:首次/重连、前次路径、后台是否退出、连接开始时刻、应用显示路径、
|
||||
最终抓包确认路径、首次直接探测、首个直接接收、首次持续直连数据、端口数、
|
||||
目标端口集合与顺序、每端口重试次数、失败/跳过原因,以及抓包与版本缺测项。
|
||||
|
||||
分析采用以下规则:
|
||||
|
||||
- 通过双端地址转换前后的同一载荷、时序及所属进程核对真实直连。仅 UI 标记、
|
||||
低 RTT、单个短包或与某服务器交换数据,均不足以证明直连。空闲保活不能当作
|
||||
新远控会话的数据;需关联连接后的持续传输。
|
||||
- CrossDesk 分开统计普通 ICE 直连、增强直连、中继、协商未启动、探测未命中、
|
||||
命中后验证失败;命中成功仍使用仓库 wire 核验。UU 的内部日志契约与密钥未知,
|
||||
不直接套用 `verify_punch_wire.py`,不把无法解读的包伪装为认证检查成功。
|
||||
- 若 UU 重连沿用空闲期持续存活的直连端点,首次连接却要重新探测,优先检查通道
|
||||
保持/复用差异。若进程重启后也稳定优于 CrossDesk,则重点对比目标端口分布、
|
||||
双端发包相对顺序、重复探索与失败后的后续尝试;不能只比较 231/49 和 512/64。
|
||||
- UU 若选择 IPv6/TCP,单独报告该路径成功;不混为 CrossDesk v1 的 IPv4 UDP
|
||||
穿透成功,也不从结果中删除。判断某类协议完全没有流量前,先核对抓包覆盖。
|
||||
- 本轮结束后先形成有证据的差异和待验证假设,再决定单因素改动。不同端口策略、
|
||||
重试策略和通道保持应分别验证;不通过延长原 ICE 窗口、强制选路或重启 DTLS
|
||||
绕过接入。需要更改既定资源/协议边界时先修订对应方案。
|
||||
|
||||
正式收益评估仍使用 [v1 实验步骤](../deps/submodules/minirtc/tests/ice/lab/punch-v1-experiments.md)
|
||||
的 A/B/C 各至少 30 次;本轮保留进程的重连样本存在关联,不当作独立冷启动样本
|
||||
计算普遍成功率。若要量化 UU 与 CrossDesk 差距,还需另行制定双方启动条件一致、
|
||||
覆盖多组网络的对照样本,不由这 8 次诊断推导百分比结论。
|
||||
|
||||
## 7. U01 已收证据(2026-09-14)
|
||||
|
||||
Windows 附件覆盖 14:52:55–14:53:53;Mac 有 14:46:37–14:52:10、
|
||||
14:52:51–14:53:49 两段,只有后半段具有对应 Windows 抓包。时间为 Asia/Shanghai,
|
||||
两端墙钟约差 1.36 秒,耗时使用单端时钟或同载荷校准。
|
||||
|
||||
后半段确认 UU 新建直连:Mac 复用同一本地 UDP 端点探测 475 个不同公网端口,
|
||||
按端口数值递增发送;Windows 使用 150 个不同本地端口向 Mac 固定公网端点探测,
|
||||
共 303 个请求,按首次发包排序的第 59 个端点命中。命中后同一端点继续进行 STUN
|
||||
检查和提名,并持续承载双端能够匹配的实际数据。Mac 的 UU 日志也记录 relay →
|
||||
直连候选对切换。从 Mac 增强探测开始到首次直接入站约 5.14 秒,到 RTP 形状数据
|
||||
约 5.23 秒;不是从用户点击连接开始计时。
|
||||
|
||||
较早一次 UU 连接仅有 Mac 侧证据:223 个不同探测目标,约 2.39 秒首次命中。
|
||||
两次的 Mac 本地/公网端点及远端公网端点不同,日志显示分别创建 transport 并重新
|
||||
探测。后半段没有沿用前一次成功的传输 tuple,但未证明两端进程已经完全退出。
|
||||
上述端口数量均为这次观测值,不是固定配置或最优参数。
|
||||
|
||||
同时间 CrossDesk 日志及抓包显示还有活动远控连接,UU 也枚举了 TUN 候选;缺少
|
||||
完整操作事件、Windows 较早抓包和进程生命周期证据。因此本批作为机制诊断样本,
|
||||
不判为严格 U01 首次/重连对照通过,不计算正式成功率,也不据此修改资源预算。
|
||||
本次命中端点排名 59,不能仅以 150 与 64 的数量差归因 CrossDesk 的失败。
|
||||
|
||||
已用独立 tcpdump 过滤复核三份抓包的探测计数;后半段双端载荷匹配为 Mac →
|
||||
Windows 698/698、Windows → Mac 4284/4286,另两份为命中前短探测。52 个直接
|
||||
STUN 包的 FINGERPRINT 校验通过,含提名的请求有匹配成功响应;没有将此等同于
|
||||
UU MESSAGE-INTEGRITY HMAC 已验证。原始日志与抓包未写入仓库。
|
||||
|
||||
本机私有报告与可复现脚本位于
|
||||
`/private/tmp/crossdesk-uu-U01-analysis-3mz4000c/`。U02 补齐连续首次/重连过程;
|
||||
用户后来确认需要 UU 管理通道,后续按第 9 节调整后台条件。若已有较早 Windows
|
||||
文件,可补入 U01;本次已经确认的探测和传输证据继续保留。
|
||||
|
||||
## 8. X01 已收证据(2026-09-14)
|
||||
|
||||
Mac 同次日志包含五次连接,Windows `cd9.pcapng` 覆盖前四次。前三次双方均按
|
||||
预算发包:Mac 同一端点向 512 个随机离散目标发送,Windows 64 个本地端点各
|
||||
探测 3 次,共 192 个请求。双方均未见探测到达,10 秒探索与 3 秒收尾后保持
|
||||
中继;这些结果属于未命中,不是命中后 ICE 接入失败。
|
||||
|
||||
第四次 Mac 第 498 个目标命中 Windows 第 19 个端点;同一端点回复 ACK、继续
|
||||
完成 STUN 提名并传输。Mac 在 START 后 5.023 秒选择增强直连,最终发送 509 个
|
||||
探测后停止。双端匹配到 Mac → Windows 2574 个、反向 456 个 RTP 形状数据包;
|
||||
直接 tuple 未见 DTLS 重握手。Windows 发完三轮探测后约 2.31 秒,命中端点仍能
|
||||
接收探测并应答,说明主动发包结束没有使它提前关闭。
|
||||
|
||||
第五次在探索开始约 9.16 秒后断开;Windows 没有覆盖,Mac 只捕获前 159 个请求,
|
||||
日志记录 512 个,单独保留为取消且抓包不全,不当作完整失败。Mac 末尾抓包
|
||||
丢包计数缺失,Windows ifdrop=0。前三次失败也不能仅由内侧抓包唯一归因到 NAT
|
||||
映射或过滤规则。
|
||||
|
||||
四次完整会话的 Mac 探测均与运行时 trace 逐项匹配;系统 tcpdump 独立计数与
|
||||
解析器一致。后补 Windows `minirtc-20260914-150934.log` 覆盖第 4、5 次,第四次
|
||||
已通过仓库 `verify_punch_wire.py` 严格核验(1/1):双方认证接收 trace、各 6 个
|
||||
STUN 检查包的 MESSAGE-INTEGRITY/FINGERPRINT、提名和直接传输相互对应。
|
||||
Windows 的同一 handle 在 probe-rx 后 promote,再被 enhanced selected;停止
|
||||
探索时只保留选中端点,继续传输至断开。关闭后抓包未见原基础及池端点再出站。
|
||||
第五次取消时 Windows 日志确认额外端点计数归零,仍无对应的完整抓包。前三次
|
||||
Windows 运行日志缺测,不将第四次门禁通过扩展为全部会话的完整核验。
|
||||
|
||||
用户确认此次保留 UU 连接是操作对端所需。UU 服务日志及物理流量确认已有管理
|
||||
直连贯穿测试,应与 CrossDesk 新连接分开。此次控制方向为 Windows → Mac,
|
||||
与已分析 U01 新会话方向不同;连接间等待约 2–8 秒,未按原计划观察 60 秒、
|
||||
等待 30 秒。因此记为快速重复连接诊断,保留三次未命中、一次成功和一次不完整
|
||||
取消,不计算正式成功率。私有报告位于 `/private/tmp/crossdesk-X01-analysis-5r9xbi9i/`。
|
||||
|
||||
## 9. 保留 UU 管理通道时的后续约定
|
||||
|
||||
用户在 X01 后确认必须使用 UU 操作对端。后续优先采用可执行的条件:
|
||||
|
||||
- 保留必要的 UU 管理连接,不终止相关服务或进行会中断唯一操作通道的进程重启。
|
||||
第 3 节完全退出与安静期的步骤不适用于这种情况;标记为“管理连接保持开启的
|
||||
新会话/保留进程重连”,不标为全部进程重启,也不标为 NAT 状态已清空。
|
||||
- X02/U02 两款软件保持相同的管理连接背景。记录管理连接的端点和持续时间,
|
||||
它不计入被测新会话的成功样本。若 UU 无法在保留管理连接时建立独立测试会话,
|
||||
应标为缺测;不把原管理通道已经直连当作一次新的 UU 打洞成功。
|
||||
- 被测新会话固定控制方向。每次连接观察 60 秒,断开被测会话后保留进程等待
|
||||
30 秒,再重连观察 60 秒;两端持续抓包,最后断开被测会话后再等 15 秒停止。
|
||||
抓包需要新目录并记录实际路径,避免提前停止或覆盖文件。
|
||||
- X01 第 4 次成功样本的认证和端点生命周期证据已补齐,不要求为此重做网络过程。
|
||||
第 1–3 次 Windows 日志及第 5 次完整抓包的缺测状态继续保留。
|
||||
|
||||
这是根据操作条件对实验方法的调整,没有改变功能开关、探测预算或 ICE 接入
|
||||
规则。与完全退出后台的原始批次分开记录,不将两类条件混成一个成功率。
|
||||
|
||||
## 10. U01/X01 后的新参数批次
|
||||
|
||||
用户要求 CrossDesk 改用离散递增目标并提高数量。当前默认 pool=128、目标=1024、
|
||||
128 PPS;目的端口仍在 1024–65535 无重复随机抽取,仅发送顺序改为递增。本地
|
||||
pool 端口由系统分配,不假设本地递增能使 NAT 公网端口递增。
|
||||
|
||||
两端同步 MiniRTC 源码并重新构建、重启 CrossDesk,记录二进制 SHA 与 HELLO/START
|
||||
实际预算;显式旧环境变量和旧 manifest 会覆盖新默认值,旧参数对端协商也会降低
|
||||
预算。新批次保持第 9 节的 UU 管理通道,抓包使用新目录。核对 anchor 未命中时
|
||||
目标端口是否严格递增、pool 是否分配 128 个端点;最多 1024 次 PROBE/ACK 共享
|
||||
预算与原截止仍有效,命中后实际发送数量可以减少。
|
||||
|
||||
命中仍需关联同一 handle 的标准 ICE 检查、提名与后续传输;未命中保留中继和
|
||||
清理记录。此批同时修改数量、速率和顺序,不能单独归因排序带来的收益,也不将
|
||||
X01 的旧样本追溯计为新参数的验证。画面、声音、控制及切换质量继续按用户要求免验。
|
||||
Reference in New Issue
Block a user