refactor: handlers
This commit is contained in:
@@ -1,12 +1,11 @@
|
||||
#![cfg(target_os = "linux")]
|
||||
|
||||
use crate::audio::codec::OpusCodec;
|
||||
use crate::app::client::handlers;
|
||||
use crate::app::client::session::ClientSession;
|
||||
use crate::audio::config::AudioConfig;
|
||||
use crate::audio::player::AudioPlayer;
|
||||
use crate::audio::recorder::AudioRecorder;
|
||||
use crate::net::discovery::Discovery;
|
||||
use crate::net::network::{AudioSocket, ClientNetwork, ControlConnection};
|
||||
use crate::net::protocol::{AudioPacket, ControlPacket, DeviceInfo, RpcResult};
|
||||
use crate::net::network::{AudioSocket, ClientNetwork};
|
||||
use crate::net::protocol::{ControlPacket, DeviceInfo, RpcResult};
|
||||
use anyhow::{Context, Result};
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
@@ -14,6 +13,7 @@ use tokio::sync::broadcast;
|
||||
|
||||
pub struct Client {
|
||||
info: DeviceInfo,
|
||||
session: Arc<tokio::sync::Mutex<Option<Arc<ClientSession>>>>,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
@@ -24,10 +24,11 @@ impl Client {
|
||||
mac: mac.to_string(),
|
||||
version,
|
||||
},
|
||||
session: Arc::new(tokio::sync::Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run(self) -> Result<()> {
|
||||
pub async fn run(self: Arc<Self>) -> Result<()> {
|
||||
println!("正在寻找服务端...");
|
||||
let (server_ip, server_port) = Discovery::discover_server().await?;
|
||||
let server_addr = SocketAddr::new(server_ip, server_port);
|
||||
@@ -48,177 +49,67 @@ impl Client {
|
||||
p => return Err(anyhow::anyhow!("认证失败: {:?}", p)),
|
||||
}
|
||||
|
||||
let (reader, writer) = control.split();
|
||||
let session = Arc::new(ClientSession::new(self.info.clone(), writer));
|
||||
*self.session.lock().await = Some(session.clone());
|
||||
|
||||
let (stop_tx, _) = broadcast::channel::<()>(1);
|
||||
let audio_socket = Arc::new(AudioSocket::bind().await?);
|
||||
|
||||
// 启动测试 RPC 调用的任务
|
||||
let client_clone = self.clone();
|
||||
tokio::spawn(async move {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
||||
println!("测试向服务端发起 RPC: status");
|
||||
match client_clone.call_server("status", vec![]).await {
|
||||
Ok(res) => println!("收到服务端响应: {:?}", res),
|
||||
Err(e) => eprintln!("向服务端发起 RPC 失败: {}", e),
|
||||
}
|
||||
});
|
||||
|
||||
let mut reader = reader;
|
||||
loop {
|
||||
tokio::select! {
|
||||
packet = control.recv_packet() => {
|
||||
let packet = packet?;
|
||||
match packet {
|
||||
ControlPacket::StartRecording { config } => {
|
||||
println!("开始录音: {:?}", config);
|
||||
let socket = audio_socket.clone();
|
||||
let mut stop_rx = stop_tx.subscribe();
|
||||
let server_addr = control.peer_addr()?;
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = handle_recording(config, socket, server_addr, stop_rx).await {
|
||||
eprintln!("录音出错: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
ControlPacket::StopRecording => {
|
||||
println!("停止录音");
|
||||
let _ = stop_tx.send(());
|
||||
}
|
||||
ControlPacket::StartPlayback { config } => {
|
||||
println!("开始播放: {:?}", config);
|
||||
let socket = audio_socket.clone();
|
||||
let mut stop_rx = stop_tx.subscribe();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = handle_playback(config, socket, stop_rx).await {
|
||||
eprintln!("播放出错: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
ControlPacket::StopPlayback => {
|
||||
println!("停止播放");
|
||||
let _ = stop_tx.send(());
|
||||
}
|
||||
ControlPacket::RpcRequest { id, method, args } => {
|
||||
println!("收到 RPC 请求: {} {:?}", method, args);
|
||||
let result = handle_rpc(&method, args).await;
|
||||
control.send_packet(&ControlPacket::RpcResponse { id, result }).await?;
|
||||
}
|
||||
ControlPacket::Ping => {
|
||||
control.send_packet(&ControlPacket::Pong).await?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let packet = reader.recv_packet().await?;
|
||||
let self_clone = self.clone();
|
||||
let session_clone = session.clone();
|
||||
let audio_socket = audio_socket.clone();
|
||||
let stop_tx = stop_tx.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = handlers::handle_packet(
|
||||
self_clone,
|
||||
session_clone,
|
||||
packet,
|
||||
audio_socket,
|
||||
stop_tx,
|
||||
server_addr,
|
||||
)
|
||||
.await
|
||||
{
|
||||
eprintln!("处理控制包出错: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_recording(
|
||||
config: AudioConfig,
|
||||
socket: Arc<AudioSocket>,
|
||||
server_addr: SocketAddr,
|
||||
mut stop_rx: broadcast::Receiver<()>,
|
||||
) -> Result<()> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let recorder = AudioRecorder::new(&config)?;
|
||||
let mut codec = OpusCodec::new(&config)?;
|
||||
let mut pcm_buf = vec![0i16; config.frame_size];
|
||||
let mut opus_buf = vec![0u8; 4096];
|
||||
|
||||
loop {
|
||||
if stop_rx.try_recv().is_ok() {
|
||||
break;
|
||||
}
|
||||
let n = recorder.read(&mut pcm_buf)?;
|
||||
if n > 0 {
|
||||
let opus_len = codec.encode(&pcm_buf[..n], &mut opus_buf)?;
|
||||
let packet = AudioPacket {
|
||||
data: opus_buf[..opus_len].to_vec(),
|
||||
};
|
||||
socket.send_packet(&packet, server_addr).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
println!("当前系统不支持 ALSA 录音,模拟发送音频数据...");
|
||||
loop {
|
||||
if stop_rx.try_recv().is_ok() {
|
||||
break;
|
||||
}
|
||||
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
|
||||
let packet = AudioPacket {
|
||||
data: vec![0u8; 10],
|
||||
};
|
||||
socket.send_packet(&packet, server_addr).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_playback(
|
||||
config: AudioConfig,
|
||||
socket: Arc<AudioSocket>,
|
||||
mut stop_rx: broadcast::Receiver<()>,
|
||||
) -> Result<()> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let player = AudioPlayer::new(&config)?;
|
||||
let mut codec = OpusCodec::new(&config)?;
|
||||
let mut pcm_buf = vec![0i16; config.frame_size];
|
||||
let mut udp_buf = vec![0u8; 4096];
|
||||
|
||||
loop {
|
||||
if stop_rx.try_recv().is_ok() {
|
||||
break;
|
||||
}
|
||||
// 这里简单处理,UDP 接收可能阻塞。实际建议加超时或 select
|
||||
let (packet, _) = socket.recv_packet(&mut udp_buf).await?;
|
||||
let pcm_len = codec.decode(&packet.data, &mut pcm_buf)?;
|
||||
player.write(&pcm_buf[..pcm_len])?;
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
println!("当前系统不支持 ALSA 播放,模拟接收音频数据...");
|
||||
let mut udp_buf = vec![0u8; 4096];
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = stop_rx.recv() => break,
|
||||
res = socket.recv_packet(&mut udp_buf) => {
|
||||
let _ = res?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_rpc(method: &str, args: Vec<String>) -> RpcResult {
|
||||
if method == "shell" && !args.is_empty() {
|
||||
let cmd = &args[0];
|
||||
println!("执行 Shell: {}", cmd);
|
||||
|
||||
// 模拟执行
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
use std::process::Command;
|
||||
let output = Command::new("sh").arg("-c").arg(cmd).output();
|
||||
match output {
|
||||
Ok(out) => RpcResult {
|
||||
stdout: String::from_utf8_lossy(&out.stdout).to_string(),
|
||||
stderr: String::from_utf8_lossy(&out.stderr).to_string(),
|
||||
code: out.status.code().unwrap_or(0),
|
||||
},
|
||||
Err(e) => RpcResult {
|
||||
stdout: "".to_string(),
|
||||
stderr: e.to_string(),
|
||||
code: -1,
|
||||
},
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
RpcResult {
|
||||
stdout: format!("模拟执行 {} 成功", cmd),
|
||||
stderr: "".to_string(),
|
||||
code: 0,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RpcResult {
|
||||
stdout: "".to_string(),
|
||||
stderr: "未知方法或参数错误".to_string(),
|
||||
code: -1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_server(&self, method: &str, args: Vec<String>) -> Result<RpcResult> {
|
||||
let session = self
|
||||
.session
|
||||
.lock()
|
||||
.await
|
||||
.clone()
|
||||
.context("Session not established")?;
|
||||
let (id, rx) = session.rpc.alloc_id();
|
||||
session
|
||||
.writer
|
||||
.lock()
|
||||
.await
|
||||
.send_packet(&ControlPacket::RpcRequest {
|
||||
id,
|
||||
method: method.to_string(),
|
||||
args,
|
||||
})
|
||||
.await?;
|
||||
Ok(rx.await?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@ pub async fn run_client() -> Result<()> {
|
||||
let mac = "00:11:22:33:44:55";
|
||||
let version = 1;
|
||||
|
||||
let client = Client::new(model, mac, version);
|
||||
let client = std::sync::Arc::new(Client::new(model, mac, version));
|
||||
client.run().await
|
||||
}
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
#![cfg(target_os = "linux")]
|
||||
|
||||
use crate::app::client::core::Client;
|
||||
use crate::app::client::session::ClientSession;
|
||||
use crate::audio::codec::OpusCodec;
|
||||
use crate::audio::config::AudioConfig;
|
||||
use crate::audio::player::AudioPlayer;
|
||||
use crate::audio::recorder::AudioRecorder;
|
||||
use crate::net::network::AudioSocket;
|
||||
use crate::net::protocol::{AudioPacket, ControlPacket, RpcResult};
|
||||
use anyhow::Result;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::broadcast;
|
||||
|
||||
pub async fn handle_packet(
|
||||
client: Arc<Client>,
|
||||
session: Arc<ClientSession>,
|
||||
packet: ControlPacket,
|
||||
audio_socket: Arc<AudioSocket>,
|
||||
stop_tx: broadcast::Sender<()>,
|
||||
server_addr: SocketAddr,
|
||||
) -> Result<()> {
|
||||
match packet {
|
||||
ControlPacket::RpcResponse { id, result } => {
|
||||
session.rpc.fulfill(id, result);
|
||||
}
|
||||
ControlPacket::RpcRequest { id, method, args } => {
|
||||
println!("收到 RPC 请求: {} {:?}", method, args);
|
||||
let result = handle_rpc(&method, args).await;
|
||||
let response = ControlPacket::RpcResponse { id, result };
|
||||
session.writer.lock().await.send_packet(&response).await?;
|
||||
}
|
||||
ControlPacket::StartRecording { config } => {
|
||||
// ...
|
||||
println!("开始录音: {:?}", config);
|
||||
let mut stop_rx = stop_tx.subscribe();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = handle_recording(config, audio_socket, server_addr, stop_rx).await {
|
||||
eprintln!("录音出错: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
ControlPacket::StopRecording => {
|
||||
println!("停止录音");
|
||||
let _ = stop_tx.send(());
|
||||
}
|
||||
ControlPacket::StartPlayback { config } => {
|
||||
println!("开始播放: {:?}", config);
|
||||
let mut stop_rx = stop_tx.subscribe();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = handle_playback(config, audio_socket, stop_rx).await {
|
||||
eprintln!("播放出错: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
ControlPacket::StopPlayback => {
|
||||
println!("停止播放");
|
||||
let _ = stop_tx.send(());
|
||||
}
|
||||
ControlPacket::Ping => {
|
||||
let _ = session
|
||||
.writer
|
||||
.lock()
|
||||
.await
|
||||
.send_packet(&ControlPacket::Pong)
|
||||
.await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_rpc(method: &str, args: Vec<String>) -> RpcResult {
|
||||
match method {
|
||||
"shell" => handle_shell(args).await,
|
||||
_ => RpcResult {
|
||||
stdout: "".to_string(),
|
||||
stderr: format!("Unknown method: {}", method),
|
||||
code: -1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_shell(args: Vec<String>) -> RpcResult {
|
||||
if args.is_empty() {
|
||||
return RpcResult {
|
||||
stdout: "".to_string(),
|
||||
stderr: "Missing command argument".to_string(),
|
||||
code: -1,
|
||||
};
|
||||
}
|
||||
let cmd = &args[0];
|
||||
println!("Executing Shell: {}", cmd);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
use std::process::Command;
|
||||
let output = Command::new("sh").arg("-c").arg(cmd).output();
|
||||
match output {
|
||||
Ok(out) => RpcResult {
|
||||
stdout: String::from_utf8_lossy(&out.stdout).to_string(),
|
||||
stderr: String::from_utf8_lossy(&out.stderr).to_string(),
|
||||
code: out.status.code().unwrap_or(0),
|
||||
},
|
||||
Err(e) => RpcResult {
|
||||
stdout: "".to_string(),
|
||||
stderr: e.to_string(),
|
||||
code: -1,
|
||||
},
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
RpcResult {
|
||||
stdout: format!("Mock execution of {} successful", cmd),
|
||||
stderr: "".to_string(),
|
||||
code: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_recording(
|
||||
config: AudioConfig,
|
||||
socket: Arc<AudioSocket>,
|
||||
server_addr: SocketAddr,
|
||||
mut stop_rx: broadcast::Receiver<()>,
|
||||
) -> Result<()> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let recorder = AudioRecorder::new(&config)?;
|
||||
let mut codec = OpusCodec::new(&config)?;
|
||||
let mut pcm_buf = vec![0i16; config.frame_size];
|
||||
let mut opus_buf = vec![0u8; 4096];
|
||||
|
||||
loop {
|
||||
if stop_rx.try_recv().is_ok() {
|
||||
break;
|
||||
}
|
||||
let n = recorder.read(&mut pcm_buf)?;
|
||||
if n > 0 {
|
||||
let opus_len = codec.encode(&pcm_buf[..n], &mut opus_buf)?;
|
||||
let packet = AudioPacket {
|
||||
data: opus_buf[..opus_len].to_vec(),
|
||||
};
|
||||
socket.send_packet(&packet, server_addr).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
println!("当前系统不支持 ALSA 录音,模拟发送音频数据...");
|
||||
loop {
|
||||
if stop_rx.try_recv().is_ok() {
|
||||
break;
|
||||
}
|
||||
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
|
||||
let packet = AudioPacket {
|
||||
data: vec![0u8; 10],
|
||||
};
|
||||
socket.send_packet(&packet, server_addr).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_playback(
|
||||
config: AudioConfig,
|
||||
socket: Arc<AudioSocket>,
|
||||
mut stop_rx: broadcast::Receiver<()>,
|
||||
) -> Result<()> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let player = AudioPlayer::new(&config)?;
|
||||
let mut codec = OpusCodec::new(&config)?;
|
||||
let mut pcm_buf = vec![0i16; config.frame_size];
|
||||
let mut udp_buf = vec![0u8; 4096];
|
||||
|
||||
loop {
|
||||
if stop_rx.try_recv().is_ok() {
|
||||
break;
|
||||
}
|
||||
let (packet, _) = socket.recv_packet(&mut udp_buf).await?;
|
||||
let pcm_len = codec.decode(&packet.data, &mut pcm_buf)?;
|
||||
player.write(&pcm_buf[..pcm_len])?;
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
println!("当前系统不支持 ALSA 播放,模拟接收音频数据...");
|
||||
let mut udp_buf = vec![0u8; 4096];
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = stop_rx.recv() => break,
|
||||
res = socket.recv_packet(&mut udp_buf) => {
|
||||
let _ = res?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,2 +1,4 @@
|
||||
pub mod core;
|
||||
pub mod entry;
|
||||
pub mod handlers;
|
||||
pub mod session;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
use crate::net::network::ControlWriter;
|
||||
use crate::net::protocol::DeviceInfo;
|
||||
use crate::net::rpc::RpcManager;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct ClientSession {
|
||||
pub info: DeviceInfo,
|
||||
pub writer: Arc<tokio::sync::Mutex<ControlWriter>>,
|
||||
pub rpc: Arc<RpcManager>,
|
||||
}
|
||||
|
||||
impl ClientSession {
|
||||
pub fn new(info: DeviceInfo, writer: ControlWriter) -> Self {
|
||||
Self {
|
||||
info,
|
||||
writer: Arc::new(tokio::sync::Mutex::new(writer)),
|
||||
rpc: Arc::new(RpcManager::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user