Milestone 1

This commit is contained in:
sam
2026-05-03 11:24:13 +02:00
parent 7dbb940107
commit 43483c2145
16 changed files with 1576 additions and 20 deletions

14
client_node/Cargo.toml Normal file
View File

@@ -0,0 +1,14 @@
[package]
name = "client_node"
version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1.0.102"
core_protocol = { version = "0.1.0", path = "../core_protocol" }
futures = "0.3.32"
tokio = { version = "1.52.1", features = ["rt-multi-thread", "net", "macros"] }
tokio-serde = { version = "0.9.0", features = ["bincode"] }
tokio-util = { version = "0.7.18", features = ["codec"] }
tracing = "0.1.44"
tracing-subscriber = "0.3.23"

24
client_node/src/main.rs Normal file
View File

@@ -0,0 +1,24 @@
//! Client Node entry point.
//!
//! This module initializes the desktop client application, sets up the Tokio
//! background thread for networking, and eventually binds to the UI framework.
#![forbid(unsafe_code)]
#![deny(clippy::all, clippy::pedantic)]
#![deny(clippy::unwrap_used, clippy::expect_used)]
mod network;
use tracing::{error, info};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init();
info!("Starting client node...");
if let Err(e) = network::control::connect_and_auth("TestUser").await {
error!("Connection error: {:?}", e);
}
Ok(())
}

View File

@@ -0,0 +1,72 @@
//! Reliable TCP connectivity logic.
//!
//! Implements the client-side TCP connection handling, including framing,
//! serialization, and the initial authentication handshake.
use anyhow::{Context, Result};
use core_protocol::constants;
use core_protocol::tcp_events::TcpEvent;
use futures::{SinkExt, StreamExt};
use tokio::net::TcpStream;
use tokio_serde::formats::Bincode;
use tokio_serde::SymmetricallyFramed;
use tokio_util::codec::{Framed, LengthDelimitedCodec};
use tracing::{info, warn};
/// A type alias for the symmetrically framed TCP stream to simplify syntax.
type FramedStream = SymmetricallyFramed<
Framed<TcpStream, LengthDelimitedCodec>,
TcpEvent,
Bincode<TcpEvent, TcpEvent>,
>;
/// Connects to the server and performs the initial authentication handshake.
///
/// This establishes a length-delimited TCP connection to prevent fragmentation,
/// sends an `AuthRequest` with the given username, and awaits the `AuthResponse`.
///
/// # Arguments
/// * `username` - The requested display name for this client.
///
/// # Errors
/// Returns an `anyhow::Result` if the TCP socket cannot connect, if the serialization/
/// deserialization fails, or if the server closes the connection prematurely.
pub async fn connect_and_auth(username: &str) -> Result<()> {
let addr = format!("127.0.0.1:{}", constants::TCP_PORT);
info!("Connecting to server at {}...", addr);
let stream = TcpStream::connect(&addr).await.context("Failed to connect to server")?;
info!("Connected!");
// Construct the codec pipeline exactly mirroring the server's configuration
// to ensure reliable packet framing.
let length_delimited = Framed::new(stream, LengthDelimitedCodec::new());
let mut framed: FramedStream = SymmetricallyFramed::new(
length_delimited,
Bincode::<TcpEvent, TcpEvent>::default(),
);
let auth_req = TcpEvent::AuthRequest {
username: username.to_string(),
};
framed.send(auth_req).await.context("Failed to send AuthRequest")?;
info!("Sent AuthRequest for user: {}", username);
if let Some(response) = framed.next().await {
let response = response.context("Failed to deserialize response")?;
match response {
TcpEvent::AuthResponse { session_token } => {
info!("Successfully authenticated! Session token: {}", session_token);
}
_ => {
warn!("Received unexpected event instead of AuthResponse");
}
}
} else {
warn!("Connection closed before receiving AuthResponse");
}
Ok(())
}

View File

@@ -0,0 +1,6 @@
//! Internet connectivity modules.
//!
//! This module isolates all remote networking logic from the UI and audio engines.
//! It exposes the necessary interfaces to manage TCP control lanes and UDP streams.
pub mod control;