Setup a server-client program.

I setup a server-client program where the server will terminate when
Ctrl+C is pressed.
This commit is contained in:
Myrddin Dundragon 2025-05-08 20:39:54 -04:00
parent 15ae16abb0
commit a965f796ae
6 changed files with 1657 additions and 7 deletions

1546
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package]
name = "satellite"
version = "0.0.0"
version = "0.1.0"
edition = "2021"
description = "Rust Challenge"
repository = "/myrddin/satellite"
@ -10,4 +10,18 @@ license-file = "LICENSE.md"
[[bin]]
name = "satellite"
path = "src/server.rs"
[[bin]]
name = "base_station"
path = "src/client.rs"
[dependencies]
clap = { version = "4.5.32", features = ["derive"] }
chrono = "0.4.40"
tokio = { version = "1.44.1", features = ["macros", "rt-multi-thread", "io-std", "fs", "signal", "sync"] }
console-subscriber = "0.4.1"

View File

@ -1,3 +1,7 @@
# satellite
# Satellite Propulsion Simulation
Rust Challenge
## Design Approach
## Instructions
### Build
### Run

View File

@ -1,15 +1,15 @@
//! Rust Challenge
mod project;
/// The environment variable defined by Cargo for the name.
const CLIENT_NAME: &'static str = "base_station";
/// Print the version of the project.
fn print_version()
{
println!("{} v{}", project::get_name(), project::get_version());
println!("{} v{}", CLIENT_NAME, project::get_version());
}

View File

@ -3,6 +3,7 @@
/// The environment variable defined by Cargo for the name.
#[allow(dead_code)]
const NAME: Option<&str> = option_env!("CARGO_PKG_NAME");
/// The environment variable defined by Cargo for the version.
@ -17,6 +18,7 @@ const NOT_DEFINED: &'static str = "UNDEFINED";
/// set at compile time and comes from the Cargo.toml file.
///
/// If a value is not found, then it will return the not defined value.
#[allow(dead_code)]
pub fn get_name() -> &'static str
{
NAME.unwrap_or(NOT_DEFINED)

84
src/server.rs Normal file
View File

@ -0,0 +1,84 @@
//! Rust Challenge: Satellite Propulsion Sim
mod project;
use std::io;
use tokio::sync::watch;
type IoResult<T> = Result<T, io::Error>;
/// Print the version of the project.
fn print_version()
{
println!("{} v{}", project::get_name(), project::get_version());
}
/// Asynchronously listen for CTRL-C to be pressed.
///
/// Sends `false` through the watch channel when the signal is received.
async fn listen_for_termination(sender: watch::Sender<bool>) -> IoResult<()>
{
tokio::signal::ctrl_c().await?;
println!("\nReceived Ctrl+C, initiating shutdown...");
// Send termination message to all listeners
sender.send_replace(false);
sender.closed().await;
Ok(())
}
/// Program entry point.
#[tokio::main]
async fn main() -> IoResult<()>
{
print_version();
let (term_sender, mut term_receiver) = watch::channel(true);
// Spawn a task to handle termination signal.
let term_task =
tokio::spawn(async move { listen_for_termination(term_sender).await });
// Spawn a task to listen for network communication.
let comm_task = tokio::spawn(async move {
let mut running = true;
while running
{
tokio::select! {
_ = term_receiver.changed() => {
println!("Communication task received shutdown message.");
running = *term_receiver.borrow_and_update();
}
}
}
});
// Wait for termination signal task and propagate any error.
match term_task.await
{
Ok(Ok(())) =>
{}
Ok(Err(e)) => return Err(e),
Err(e) =>
{
eprintln!("Termination task panicked: {}", e);
return Err(io::Error::new(io::ErrorKind::Other,
"Termination task panicked"));
}
}
// Wait for the communication task to complete.
let _ = comm_task.await;
println!("Shutdown complete.");
Ok(())
}