diff options
| author | salmonik <raisersoft@proton.me> | 2026-02-28 16:14:18 +0100 |
|---|---|---|
| committer | salmonik <raisersoft@proton.me> | 2026-02-28 16:14:18 +0100 |
| commit | 63a3c3e4be75c757383be46e196a4462d8da818b (patch) | |
| tree | b21b78b371e1e16d7b76e1804286f75945e8fa93 | |
| download | zagan-bet-63a3c3e4be75c757383be46e196a4462d8da818b.tar.gz zagan-bet-63a3c3e4be75c757383be46e196a4462d8da818b.zip | |
init version
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Cargo.lock | 32 | ||||
| -rw-r--r-- | Cargo.toml | 7 | ||||
| -rw-r--r-- | src/bin/client.rs | 98 | ||||
| -rw-r--r-- | src/bin/server.rs | 66 |
5 files changed, 204 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| /target | |||
diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..17b9b81 --- /dev/null +++ b/Cargo.lock | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | # This file is automatically @generated by Cargo. | ||
| 2 | # It is not intended for manual editing. | ||
| 3 | version = 4 | ||
| 4 | |||
| 5 | [[package]] | ||
| 6 | name = "winapi" | ||
| 7 | version = "0.3.9" | ||
| 8 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 9 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" | ||
| 10 | dependencies = [ | ||
| 11 | "winapi-i686-pc-windows-gnu", | ||
| 12 | "winapi-x86_64-pc-windows-gnu", | ||
| 13 | ] | ||
| 14 | |||
| 15 | [[package]] | ||
| 16 | name = "winapi-i686-pc-windows-gnu" | ||
| 17 | version = "0.4.0" | ||
| 18 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 19 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" | ||
| 20 | |||
| 21 | [[package]] | ||
| 22 | name = "winapi-x86_64-pc-windows-gnu" | ||
| 23 | version = "0.4.0" | ||
| 24 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 25 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" | ||
| 26 | |||
| 27 | [[package]] | ||
| 28 | name = "zagan-bet" | ||
| 29 | version = "0.1.0" | ||
| 30 | dependencies = [ | ||
| 31 | "winapi", | ||
| 32 | ] | ||
diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..33903f4 --- /dev/null +++ b/Cargo.toml | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | [package] | ||
| 2 | name = "zagan-bet" | ||
| 3 | version = "0.1.0" | ||
| 4 | edition = "2024" | ||
| 5 | |||
| 6 | [dependencies] | ||
| 7 | winapi = { version = "0.3", features = ["consoleapi", "processenv", "wincon", "minwindef", "winbase"] } | ||
diff --git a/src/bin/client.rs b/src/bin/client.rs new file mode 100644 index 0000000..45c5090 --- /dev/null +++ b/src/bin/client.rs | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | use std::io::{BufRead, BufReader, Write, stdin, stdout}; | ||
| 2 | use std::net::TcpStream; | ||
| 3 | use std::time::Instant; | ||
| 4 | |||
| 5 | #[cfg(windows)] | ||
| 6 | mod win { | ||
| 7 | use std::io; | ||
| 8 | use std::os::windows::io::AsRawHandle; | ||
| 9 | use winapi::um::consoleapi::GetConsoleMode; | ||
| 10 | use winapi::um::consoleapi::SetConsoleMode; | ||
| 11 | use winapi::um::wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING; | ||
| 12 | use winapi::um::winbase::STD_OUTPUT_HANDLE; | ||
| 13 | use winapi::um::processenv::GetStdHandle; | ||
| 14 | use winapi::shared::minwindef::DWORD; | ||
| 15 | |||
| 16 | pub fn enable_vt() -> io::Result<()> { | ||
| 17 | unsafe { | ||
| 18 | let h = GetStdHandle(STD_OUTPUT_HANDLE); | ||
| 19 | if h.is_null() { return Err(io::Error::last_os_error()); } | ||
| 20 | |||
| 21 | let mut mode: DWORD = 0; | ||
| 22 | if GetConsoleMode(h, &mut mode) == 0 { | ||
| 23 | return Err(io::Error::last_os_error()); | ||
| 24 | } | ||
| 25 | let new = mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING; | ||
| 26 | if SetConsoleMode(h, new) == 0 { | ||
| 27 | return Err(io::Error::last_os_error()); | ||
| 28 | } | ||
| 29 | Ok(()) | ||
| 30 | } | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | fn main() { | ||
| 35 | #[cfg(windows)] | ||
| 36 | win::enable_vt().unwrap(); | ||
| 37 | |||
| 38 | let mut addr: Option<String>; | ||
| 39 | let mut stream: Option<TcpStream> = None; | ||
| 40 | loop { | ||
| 41 | print!("Command: "); | ||
| 42 | stdout().flush().unwrap(); | ||
| 43 | let mut line = String::new(); | ||
| 44 | stdin().read_line(&mut line).unwrap(); | ||
| 45 | let line = line.trim(); | ||
| 46 | if line.is_empty() { | ||
| 47 | continue; | ||
| 48 | } | ||
| 49 | let parts: Vec<&str> = line.splitn(2, ' ').collect(); | ||
| 50 | match parts[0] { | ||
| 51 | "connect" => { | ||
| 52 | if parts.len() < 2 { | ||
| 53 | println!("Usage: connect <addr>"); | ||
| 54 | continue; | ||
| 55 | } | ||
| 56 | addr = Some(parts[1].to_string()); | ||
| 57 | match TcpStream::connect(addr.as_ref().unwrap()) { | ||
| 58 | Ok(s) => { | ||
| 59 | let mut ping_writer = s.try_clone().unwrap(); | ||
| 60 | let mut ping_reader = BufReader::new(s.try_clone().unwrap()); | ||
| 61 | let start = Instant::now(); | ||
| 62 | writeln!(ping_writer, "PING").unwrap(); | ||
| 63 | let mut response = String::new(); | ||
| 64 | ping_reader.read_line(&mut response).unwrap(); | ||
| 65 | let elapsed = start.elapsed(); | ||
| 66 | println!("Connected to {} with ping delay: {:?}", addr.as_ref().unwrap(), elapsed); | ||
| 67 | stream = Some(s); | ||
| 68 | } | ||
| 69 | Err(e) => { | ||
| 70 | println!("Failed to connect: {}", e); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | } | ||
| 74 | "send" => { | ||
| 75 | if stream.is_none() { | ||
| 76 | println!("Not connected. Use 'connect <addr>' first."); | ||
| 77 | continue; | ||
| 78 | } | ||
| 79 | if parts.len() < 2 { | ||
| 80 | println!("Usage: send <message>"); | ||
| 81 | continue; | ||
| 82 | } | ||
| 83 | let msg = parts[1]; | ||
| 84 | let s = stream.as_mut().unwrap(); | ||
| 85 | let writer_stream = s.try_clone().unwrap(); | ||
| 86 | let mut writer = writer_stream; | ||
| 87 | let response_reader_stream = s.try_clone().unwrap(); | ||
| 88 | let mut response_reader = BufReader::new(response_reader_stream); | ||
| 89 | writeln!(writer, "{}", msg).unwrap(); | ||
| 90 | let mut response = String::new(); | ||
| 91 | response_reader.read_line(&mut response).unwrap(); | ||
| 92 | println!("Echo: {}", response.trim()); | ||
| 93 | } | ||
| 94 | "quit" => break, | ||
| 95 | _ => println!("Unknown command. Available: connect, send, quit"), | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } | ||
diff --git a/src/bin/server.rs b/src/bin/server.rs new file mode 100644 index 0000000..215d2ae --- /dev/null +++ b/src/bin/server.rs | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | use std::io::{BufRead, BufReader, Write}; | ||
| 2 | use std::net::TcpListener; | ||
| 3 | use std::thread; | ||
| 4 | |||
| 5 | fn handle_client(stream: std::net::TcpStream) { | ||
| 6 | println!("Client connected"); | ||
| 7 | let reader_stream = stream.try_clone().unwrap(); | ||
| 8 | let reader = BufReader::new(reader_stream); | ||
| 9 | let mut writer = stream; | ||
| 10 | for line in reader.lines() { | ||
| 11 | let line = line.unwrap(); | ||
| 12 | println!("Received: {}", line); | ||
| 13 | writeln!(writer, "{}", line).unwrap(); | ||
| 14 | } | ||
| 15 | println!("Client disconnected"); | ||
| 16 | } | ||
| 17 | |||
| 18 | fn main() { | ||
| 19 | let args: Vec<String> = std::env::args().collect(); | ||
| 20 | let mut ip = "127.0.0.1".to_string(); | ||
| 21 | let mut port = 8080; | ||
| 22 | let mut i = 1; | ||
| 23 | while i < args.len() { | ||
| 24 | if args[i] == "-p" { | ||
| 25 | if i + 1 < args.len() { | ||
| 26 | port = args[i + 1].parse::<u16>().unwrap_or_else(|_| { | ||
| 27 | eprintln!("Invalid port number: {}", args[i + 1]); | ||
| 28 | std::process::exit(1); | ||
| 29 | }); | ||
| 30 | i += 2; | ||
| 31 | } else { | ||
| 32 | eprintln!("-p requires a port number"); | ||
| 33 | std::process::exit(1); | ||
| 34 | } | ||
| 35 | } else if args[i] == "-i" { | ||
| 36 | if i + 1 < args.len() { | ||
| 37 | ip = args[i + 1].clone(); | ||
| 38 | if ip != "127.0.0.1" && ip != "0.0.0.0" { | ||
| 39 | eprintln!("Invalid IP address: {}. Must be 127.0.0.1 or 0.0.0.0", ip); | ||
| 40 | std::process::exit(1); | ||
| 41 | } | ||
| 42 | i += 2; | ||
| 43 | } else { | ||
| 44 | eprintln!("-i requires an IP address"); | ||
| 45 | std::process::exit(1); | ||
| 46 | } | ||
| 47 | } else { | ||
| 48 | eprintln!("Unknown argument: {}", args[i]); | ||
| 49 | std::process::exit(1); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | let listener = match TcpListener::bind(format!("{}:{}", ip, port)) { | ||
| 53 | Ok(l) => l, | ||
| 54 | Err(e) => { | ||
| 55 | eprintln!("Failed to bind to {}:{}: {}", ip, port, e); | ||
| 56 | std::process::exit(1); | ||
| 57 | } | ||
| 58 | }; | ||
| 59 | println!("Server listening on {}", listener.local_addr().unwrap()); | ||
| 60 | for stream in listener.incoming() { | ||
| 61 | let stream = stream.unwrap(); | ||
| 62 | thread::spawn(move || { | ||
| 63 | handle_client(stream); | ||
| 64 | }); | ||
| 65 | } | ||
| 66 | } | ||
