From 11e66d80d4edafbfc0df475ab5b346a58856d4cd Mon Sep 17 00:00:00 2001 From: Alexander Baranov Date: Wed, 25 Feb 2026 01:18:03 +0300 Subject: [PATCH] homework: power socket tokio client --- smart-house/Cargo.lock | 151 +++++++++++++++++++++++ smart-house/Cargo.toml | 1 + smart-house/src/bin/power_socket_mock.rs | 72 +++++------ smart-house/src/main.rs | 10 +- 4 files changed, 191 insertions(+), 43 deletions(-) diff --git a/smart-house/Cargo.lock b/smart-house/Cargo.lock index 214a382..9e774f3 100644 --- a/smart-house/Cargo.lock +++ b/smart-house/Cargo.lock @@ -2,6 +2,157 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "mio" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + [[package]] name = "smart-house" version = "0.0.0" +dependencies = [ + "tokio", +] + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" diff --git a/smart-house/Cargo.toml b/smart-house/Cargo.toml index 743f6a1..e99beb3 100644 --- a/smart-house/Cargo.toml +++ b/smart-house/Cargo.toml @@ -4,3 +4,4 @@ name = "smart-house" version = "0.0.0" [dependencies] +tokio = { version = "1.49.0", features = ["rt", "net", "io-util", "time", "sync"] } diff --git a/smart-house/src/bin/power_socket_mock.rs b/smart-house/src/bin/power_socket_mock.rs index fecb398..5b64a32 100644 --- a/smart-house/src/bin/power_socket_mock.rs +++ b/smart-house/src/bin/power_socket_mock.rs @@ -1,8 +1,7 @@ -use std::io; -use std::io::{Read, Write}; -use std::net::{SocketAddr, TcpListener, TcpStream}; -use std::sync::{Arc, RwLock}; +use std::net::SocketAddr; +use std::sync::Arc; use std::time::Duration; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; fn parse_args() -> SocketAddr { let mut args = std::env::args(); @@ -13,6 +12,7 @@ fn parse_args() -> SocketAddr { .expect("server address should be valid") } +#[derive(Debug)] struct RealPowerSocket { power_rate: f32, on: bool, @@ -23,46 +23,40 @@ const CMD_TURN_ON: u8 = 2; const CMD_TURN_OFF: u8 = 3; const CMD_GET_POWER: u8 = 4; -fn handle_connection(mut stream: TcpStream, real_power_socket: Arc>) { +const TIMEOUT: Duration = Duration::from_secs(5); + +async fn handle_connection(mut socket: tokio::net::TcpStream, real_power_socket: Arc>) -> Result<(), std::io::Error> { + let mut buf = [0u8; 1]; loop { - let mut buf = [0u8; 1]; - let result = stream.read_exact(&mut buf); - match result { - Err(e) if e.kind() == io::ErrorKind::WouldBlock => { - std::thread::sleep(Duration::from_millis(250)); - continue; - } - Err(e) if [io::ErrorKind::UnexpectedEof, io::ErrorKind::ConnectionReset].contains(&e.kind()) => { - println!("connection is over"); - break; - } - Err(e) => panic!("error on reading socket: {e}"), - Ok(_) => {} + let read = tokio::time::timeout(TIMEOUT, socket.read(&mut buf)).await??; + if read == 0 { + println!("connection closed"); + return Ok(()); } match buf { [CMD_GET_ON] => { println!("handling CMD_GET_ON"); - let power_socket = real_power_socket.read().expect("power socket lock is poisoned"); + let power_socket = real_power_socket.try_read().map_err(|e| std::io::Error::other(e))?; buf = if power_socket.on { [1u8; 1] } else { [0u8; 1] }; - stream.write_all(&buf).expect("response write error"); + let _ = tokio::time::timeout(TIMEOUT, socket.write(&buf)).await?; } [CMD_TURN_ON] => { println!("handling CMD_TURN_ON"); - let mut power_socket = real_power_socket.write().expect("power socket lock is poisoned"); + let mut power_socket = real_power_socket.try_write().map_err(|e| std::io::Error::other(e))?; power_socket.on = true; - stream.write_all(&buf).expect("response write error"); + let _ = tokio::time::timeout(TIMEOUT, socket.write(&buf)).await?; } [CMD_TURN_OFF] => { println!("handling CMD_TURN_OFF"); - let mut power_socket = real_power_socket.write().expect("power socket lock is poisoned"); + let mut power_socket = real_power_socket.try_write().map_err(|e| std::io::Error::other(e))?; power_socket.on = false; - stream.write_all(&buf).expect("response write error"); + let _ = tokio::time::timeout(TIMEOUT, socket.write(&buf)).await?; } [CMD_GET_POWER] => { println!("handling CMD_GET_POWER"); - let power_socket = real_power_socket.write().expect("power socket lock is poisoned"); + let power_socket = real_power_socket.try_read().map_err(|e| std::io::Error::other(e))?; let data_buf = power_socket.power_rate.to_le_bytes(); - stream.write_all(&data_buf).expect("response write error"); + let _ = tokio::time::timeout(TIMEOUT, socket.write(&data_buf)).await?; } _ => { println!("unknown command {} - ignore it", buf[0]); @@ -71,19 +65,17 @@ fn handle_connection(mut stream: TcpStream, real_power_socket: Arc Result<(), Box> { + let real_power_socket = Arc::new(tokio::sync::RwLock::new(RealPowerSocket { power_rate: 12.0, on: false })); + let rt = tokio::runtime::Builder::new_current_thread().enable_all().build()?; + rt.block_on(async { + let listener = tokio::net::TcpListener::bind(parse_args()).await?; -fn main() { - let real_power_socket = Arc::new(RwLock::new(RealPowerSocket { power_rate: 12.0, on: false })); - let listener = TcpListener::bind(parse_args()).expect("address 127.0.0.1:10001 should be available for listening"); - for connection in listener.incoming() { - println!("new connection"); - let real_power_socket = real_power_socket.clone(); - let stream = connection.expect("connection should not fail"); - stream.set_write_timeout(Some(TIMEOUT)).expect("set_write_timeout failed"); - stream.set_nonblocking(true).expect("set_nonblocking call failed"); - std::thread::spawn(move || { - handle_connection(stream, real_power_socket); - }); - } + loop { + let (socket, _) = listener.accept().await?; + println!("new connection"); + let real_power_socket = real_power_socket.clone(); + tokio::spawn(async move { handle_connection(socket, real_power_socket).await }); + } + }) } diff --git a/smart-house/src/main.rs b/smart-house/src/main.rs index 9a492c2..1720a88 100644 --- a/smart-house/src/main.rs +++ b/smart-house/src/main.rs @@ -1,6 +1,10 @@ -use smart_house::Thermometer; +use smart_house::PowerSocket; +use std::time::Duration; fn main() { - let thermometer = Thermometer::connect("127.0.0.1:10001").unwrap(); - println!("{}", thermometer.display()); + let mut power_socket = PowerSocket::connect("127.0.0.1:10001"); + println!("{}", power_socket.display()); + std::thread::sleep(Duration::from_secs(2)); + power_socket.set_on(!power_socket.is_on()); + println!("{}", power_socket.display()); }