easyremote/crates/client-core/src/signal.rs
2026-01-04 18:15:07 +08:00

339 lines
8.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 信令客户端
use anyhow::Result;
use futures::{SinkExt, StreamExt};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::net::TcpStream;
use tokio::sync::{mpsc, RwLock};
use tokio_tungstenite::{connect_async, tungstenite::Message, MaybeTlsStream, WebSocketStream};
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
/// 信令消息
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum SignalMessage {
/// 注册设备
#[serde(rename = "register")]
Register {
device_id: String,
verification_code: String,
},
/// 心跳
#[serde(rename = "heartbeat")]
Heartbeat { device_id: String },
/// 心跳响应
#[serde(rename = "heartbeat_ack")]
HeartbeatAck,
/// 连接请求
#[serde(rename = "connect_request")]
ConnectRequest {
session_id: String,
from_device: String,
to_device: String,
verification_code: String,
},
/// 连接响应
#[serde(rename = "connect_response")]
ConnectResponse {
session_id: String,
accepted: bool,
reason: Option<String>,
},
/// SDP Offer
#[serde(rename = "offer")]
Offer {
session_id: String,
from_device: String,
to_device: String,
sdp: String,
},
/// SDP Answer
#[serde(rename = "answer")]
Answer {
session_id: String,
from_device: String,
to_device: String,
sdp: String,
},
/// ICE Candidate
#[serde(rename = "candidate")]
Candidate {
session_id: String,
from_device: String,
to_device: String,
candidate: String,
sdp_mid: Option<String>,
sdp_mline_index: Option<u32>,
},
/// 会话结束
#[serde(rename = "session_end")]
SessionEnd { session_id: String },
/// 强制下线
#[serde(rename = "force_offline")]
ForceOffline { device_id: String },
/// 错误
#[serde(rename = "error")]
Error { code: u32, message: String },
/// 设置允许远程
#[serde(rename = "set_allow_remote")]
SetAllowRemote { device_id: String, allow: bool },
/// 刷新验证码
#[serde(rename = "refresh_code")]
RefreshCode { device_id: String },
/// 验证码已刷新
#[serde(rename = "code_refreshed")]
CodeRefreshed {
device_id: String,
verification_code: String,
},
/// 屏幕帧数据
#[serde(rename = "screen_frame")]
ScreenFrame {
session_id: String,
from_device: String,
to_device: String,
width: u32,
height: u32,
data: String, // Base64 encoded JPEG
},
/// 鼠标事件
#[serde(rename = "mouse_event")]
MouseEvent {
session_id: String,
from_device: String,
to_device: String,
x: f64,
y: f64,
event_type: String, // "move", "click", "down", "up", "scroll"
button: Option<u8>,
delta: Option<f64>,
},
/// 键盘事件
#[serde(rename = "keyboard_event")]
KeyboardEvent {
session_id: String,
from_device: String,
to_device: String,
key: String,
event_type: String, // "down", "up"
},
}
/// 信令客户端
pub struct SignalClient {
device_id: String,
server_url: String,
tx: Option<mpsc::Sender<SignalMessage>>,
connected: Arc<RwLock<bool>>,
}
impl SignalClient {
pub fn new(device_id: String, server_url: String) -> Self {
Self {
device_id,
server_url,
tx: None,
connected: Arc::new(RwLock::new(false)),
}
}
/// 连接到信令服务器
pub async fn connect(
&mut self,
on_message: impl Fn(SignalMessage) + Send + 'static,
) -> Result<()> {
// 将 http:// 转换为 ws://https:// 转换为 wss://
let ws_url = self.server_url
.replace("http://", "ws://")
.replace("https://", "wss://");
let url = format!(
"{}/ws/signal?device_id={}",
ws_url, self.device_id
);
tracing::info!("Connecting to WebSocket: {}", url);
let (ws_stream, _) = connect_async(&url).await?;
let (mut write, mut read) = ws_stream.split();
let (tx, mut rx) = mpsc::channel::<SignalMessage>(32);
self.tx = Some(tx);
*self.connected.write().await = true;
let connected = self.connected.clone();
// 发送任务
tokio::spawn(async move {
while let Some(msg) = rx.recv().await {
let text = serde_json::to_string(&msg).unwrap();
if write.send(Message::Text(text)).await.is_err() {
break;
}
}
});
// 接收任务
tokio::spawn(async move {
while let Some(Ok(msg)) = read.next().await {
if let Message::Text(text) = msg {
if let Ok(signal_msg) = serde_json::from_str::<SignalMessage>(&text) {
on_message(signal_msg);
}
}
}
*connected.write().await = false;
});
Ok(())
}
/// 发送消息
pub async fn send(&self, msg: SignalMessage) -> Result<()> {
if let Some(tx) = &self.tx {
tx.send(msg).await?;
}
Ok(())
}
/// 发送心跳
pub async fn send_heartbeat(&self) -> Result<()> {
self.send(SignalMessage::Heartbeat {
device_id: self.device_id.clone(),
})
.await
}
/// 发送连接请求
pub async fn request_connect(
&self,
session_id: String,
to_device: String,
verification_code: String,
) -> Result<()> {
self.send(SignalMessage::ConnectRequest {
session_id,
from_device: self.device_id.clone(),
to_device,
verification_code,
})
.await
}
/// 发送连接响应
pub async fn respond_connect(
&self,
session_id: String,
accepted: bool,
reason: Option<String>,
) -> Result<()> {
self.send(SignalMessage::ConnectResponse {
session_id,
accepted,
reason,
})
.await
}
/// 发送 SDP Offer
pub async fn send_offer(
&self,
session_id: String,
to_device: String,
sdp: String,
) -> Result<()> {
self.send(SignalMessage::Offer {
session_id,
from_device: self.device_id.clone(),
to_device,
sdp,
})
.await
}
/// 发送 SDP Answer
pub async fn send_answer(
&self,
session_id: String,
to_device: String,
sdp: String,
) -> Result<()> {
self.send(SignalMessage::Answer {
session_id,
from_device: self.device_id.clone(),
to_device,
sdp,
})
.await
}
/// 发送 ICE Candidate
pub async fn send_candidate(
&self,
session_id: String,
to_device: String,
candidate: String,
sdp_mid: Option<String>,
sdp_mline_index: Option<u32>,
) -> Result<()> {
self.send(SignalMessage::Candidate {
session_id,
from_device: self.device_id.clone(),
to_device,
candidate,
sdp_mid,
sdp_mline_index,
})
.await
}
/// 设置允许远程
pub async fn set_allow_remote(&self, allow: bool) -> Result<()> {
self.send(SignalMessage::SetAllowRemote {
device_id: self.device_id.clone(),
allow,
})
.await
}
/// 刷新验证码
pub async fn refresh_code(&self) -> Result<()> {
self.send(SignalMessage::RefreshCode {
device_id: self.device_id.clone(),
})
.await
}
/// 结束会话
pub async fn end_session(&self, session_id: String) -> Result<()> {
self.send(SignalMessage::SessionEnd { session_id }).await
}
/// 发送屏幕帧
pub async fn send_frame(
&self,
session_id: String,
to_device: String,
width: u32,
height: u32,
jpeg_data: &[u8],
) -> Result<()> {
let data = BASE64.encode(jpeg_data);
self.send(SignalMessage::ScreenFrame {
session_id,
from_device: self.device_id.clone(),
to_device,
width,
height,
data,
})
.await
}
/// 是否已连接
pub async fn is_connected(&self) -> bool {
*self.connected.read().await
}
}