From 01b3bea1c61ece39e906374593ef98adb2f7c86f Mon Sep 17 00:00:00 2001 From: CNWei Date: Fri, 31 Jul 2026 16:38:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(server):=20=E5=88=9D=E5=A7=8B=E5=8C=96=20r?= =?UTF-8?q?tty-server=20=E9=A1=B9=E7=9B=AE=E7=BB=93=E6=9E=84=E4=B8=8E?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 ++ Cargo.toml | 31 +++++++++++++++++++++ README.md | 62 ++++++++++++++++++++++++++++++++++++++++-- src/config.rs | 0 src/main.rs | 34 +++++++++++++++++++++++ src/terminal/engine.rs | 0 src/terminal/mod.rs | 2 ++ src/terminal/pty.rs | 0 src/ws/handler.rs | 0 src/ws/mod.rs | 2 ++ src/ws/protocol.rs | 27 ++++++++++++++++++ 11 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/config.rs create mode 100644 src/main.rs create mode 100644 src/terminal/engine.rs create mode 100644 src/terminal/mod.rs create mode 100644 src/terminal/pty.rs create mode 100644 src/ws/handler.rs create mode 100644 src/ws/mod.rs create mode 100644 src/ws/protocol.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..181f3ef --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +/.idea +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..55aa6d3 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "rtty-server" +version = "0.1.0" +edition = "2024" + +[dependencies] +# 1. 异步运行时与 Web 框架 +tokio = { version = "1.38", features = ["full"] } +axum = { version = "0.7", features = ["ws"] } +tower-http = { version = "0.5", features = ["cors", "trace"] } + +# 2. PTY 驱动与终端状态引擎 +portable-pty = "0.8" +alacritty_terminal = "0.24" + +# 3. 序列化与数据传输 (支持移动端 JSON 重排协议) +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +# 4. 日志与并发工具 +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +anyhow = "1.0" +dashmap = "6.0" # 高并发线程安全的 SessionMap + + +[profile.release] +opt-level = 3 +lto = true +codegen-units = 1 +strip = true \ No newline at end of file diff --git a/README.md b/README.md index 38b8ca0..4888965 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,61 @@ -# Rtty +# 🚀 Rtty 项目设计总结与架构指南 -Rtty 是一款基于 Rust 与 Flutter 构建的高性能、跨平台虚拟终端系统,通过服务端状态保持与多端解耦渲染,实现 PC 端工业级 ANSI 兼容与移动端原生语义化重排。 \ No newline at end of file +## 📝 一句话描述 + +> **Rtty 是一款基于 Rust 与 Flutter 构建的高性能、跨平台虚拟终端系统,通过服务端状态保持与多端解耦渲染,实现 PC 端工业级 ANSI 兼容与移动端原生语义化重排。** + +--- + +## 🏗️ 系统整体架构图 + +``` + ┌───────────────────────────────┐ + │ Rtty Server (Rust 后端) │ + │ • portable-pty (进程/Shell) │ + │ • alacritty_terminal (真相源)│ + └───────────────┬───────────────┘ + │ + WebSocket (JSON / ANSI 字节流) + │ + ┌─────────────────────────┴─────────────────────────┐ + ▼ ▼ +┌───────────────────────────────┐ ┌───────────────────────────────┐ +│ Rtty Desktop (PC 客户端) │ │ Rtty Mobile (移动客户端) │ +│ • flutter_alacritty (FFI) │ │ • Flutter Native Controls │ +│ • Canvas 经典 ANSI 网格 │ │ • ListView + RichText 重排 │ +│ • 100% 工业级 ANSI 兼容 │ │ • 自适应换行 / 卡片化 UI │ +└───────────────────────────────┘ └───────────────────────────────┘ + +``` + +--- + +## 🛠️ 核心组件与职责划分 + +### 1. **服务端内核 (`rtty-server`)** + +* **`portable-pty`**:负责底层的 POSIX/Windows 伪终端创建、子进程(Shell/Bash/Zsh)拉起与文件描述符读写。 +* **`alacritty_terminal`**:作为整个系统的“真相源 (Source of Truth)”,在内存中实时解析 ANSI 序列,维护 2D 屏幕网格(Grid)、光标位置及历史回滚缓冲区,同时提供断线重连时的快照恢复(Dump Screen Snapshot)。 +* **`WebSocket (Axum/Tokio)`**:负责与各终端建立双向实时通信,分发事件流与控制信号。 + +### 2. **桌面端渲染管线 (`rtty-desktop`)** + +* **`flutter_alacritty` (Rust FFI 绑定)**:PC 端放弃纯 Dart 的轻量级解析,直接调用本地 C-Bindings 共享 `alacritty_terminal` 的 Rust 渲染引擎。 +* **渲染特性**:Canvas 纯网格高效绘制,保持对 `vim`、`htop`、`tmux` 等全屏 ANSI 软件的 100% 工业级兼容与真彩显示。 + +### 3. **移动端渲染管线 (`rtty-mobile`)** + +* **Flutter Native 组件重排**:移动端抛弃传统 2D 字符网格,采用语义化数据拆解。 +* **渲染特性**: +* **命令与输出**:封装为 `ListView` 卡片,支持文本按词软换行(Word Wrap),彻底消除横向滚动条与小字号看不清的痛点。 +* **键盘与交互**:吸底 Command Input 配合移动端专属快捷键工具栏(Keybar)。 + + + +--- + +## 💡 Rtty 的三大核心设计优势 + +1. **状态与渲染解耦**:服务端 Rust 负责维持单一终端状态,客户端按平台特性自由渲染。 +2. **前后端内核统一**:后端与 PC 端共享 `alacritty_terminal` 引擎,解析行为 100% 绝对一致。 +3. **移动端体验革新**:告别画面缩放与 TUI 错乱,用 Native UI 呈现命令行数据。 \ No newline at end of file diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e061916 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,34 @@ +mod config; +mod terminal; +mod ws; + +use axum::{routing::get, Router}; +use std::sync::Arc; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +pub struct AppState { + // 预留全局 Session 管理句柄 +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // 初始化日志 + tracing_subscriber::registry() + .with(tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "rtty_server=debug".into())) + .with(tracing_subscriber::fmt::layer()) + .init(); + + let state = Arc::new(AppState {}); + + let app = Router::new() + .route("/ws", get(ws::handler::ws_route)) + .with_state(state); + + let addr = "0.0.0.0:8080"; + tracing::info!("🚀 Rtty Server running on ws://{}", addr); + + let listener = tokio::net::TcpListener::bind(addr).await?; + axum::serve(listener, app).await?; + + Ok(()) +} \ No newline at end of file diff --git a/src/terminal/engine.rs b/src/terminal/engine.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/terminal/mod.rs b/src/terminal/mod.rs new file mode 100644 index 0000000..21856ab --- /dev/null +++ b/src/terminal/mod.rs @@ -0,0 +1,2 @@ +mod pty; +mod engine; \ No newline at end of file diff --git a/src/terminal/pty.rs b/src/terminal/pty.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/ws/handler.rs b/src/ws/handler.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/ws/mod.rs b/src/ws/mod.rs new file mode 100644 index 0000000..09404e4 --- /dev/null +++ b/src/ws/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod handler; +mod protocol; \ No newline at end of file diff --git a/src/ws/protocol.rs b/src/ws/protocol.rs new file mode 100644 index 0000000..aab2b94 --- /dev/null +++ b/src/ws/protocol.rs @@ -0,0 +1,27 @@ +use serde::{Deserialize, Serialize}; + +/// 客户端发送给 Rtty 服务端的控制指令 +#[derive(Debug, Deserialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum ClientMessage { + /// 键盘/文本输入 + Input { data: String }, + /// 客户端请求 Resize + Resize { cols: u16, rows: u16 }, + /// 客户端申明控制权 (用来解决多端控制冲突) + ClaimControl, +} + +/// 服务端推送给客户端的消息 (分别适配 PC 和 移动端) +#[derive(Debug, Serialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum ServerMessage { + /// 原生 ANSI 字节流 (供 PC 端 xterm/flutter_alacritty 渲染) + RawData { data: String }, + /// 移动端专属:解耦后的语义化 JSON 屏显数据 + MobileSnapshot { + cursor_x: usize, + cursor_y: usize, + lines: Vec, + }, +} \ No newline at end of file