This commit is contained in:
2024-09-25 14:50:44 +02:00
commit 72e07d5633
7 changed files with 214 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/target
/client/target
/server/target

7
client/Cargo.lock generated Normal file
View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "client"
version = "0.1.0"

8
client/Cargo.toml Normal file
View File

@@ -0,0 +1,8 @@
[package]
name = "client"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

70
client/src/main.rs Normal file
View File

@@ -0,0 +1,70 @@
use std::{
io::{self, ErrorKind, Read, Write}, net::{Shutdown, TcpStream}, process, thread
};
const BUFFER_SIZE: usize = 1024;
fn main() {
let mut socket = TcpStream::connect("127.0.0.1:42069").unwrap();
let read_socket = socket.try_clone().unwrap();
thread::spawn(move || {
handle(read_socket);
});
loop {
io::stdout().flush().unwrap();
let mut input = String::new();
match io::stdin().read_line(&mut input) {
Ok(n) => {
if n > 0 {
socket.write(input.as_bytes()).unwrap();
} else {
println!("read failed");
}
}
Err(e) => {
println!("Error: {}", e);
break;
}
}
}
}
fn handle(mut socket: TcpStream) {
loop {
let mut buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
match socket.read(&mut buf) {
Ok(n) => {
let msg = String::from_utf8(buf[..n].to_vec())
.unwrap()
.trim()
.to_string();
if n <= 0 {
disconnect(socket);
return;
}
if msg.is_empty() {
continue;
}
println!("{}", msg);
}
Err(ref err) if err.kind() == ErrorKind::WouldBlock => (),
Err(_) => {
disconnect(socket);
return;
}
}
}
}
fn disconnect(socket: TcpStream) {
println!("[EVENT] Disconnected");
let _ = socket.shutdown(Shutdown::Both);
process::exit(0);
}

7
server/Cargo.lock generated Normal file
View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "rust-networking"
version = "0.1.0"

8
server/Cargo.toml Normal file
View File

@@ -0,0 +1,8 @@
[package]
name = "rust-networking"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

111
server/src/main.rs Normal file
View File

@@ -0,0 +1,111 @@
use std::collections::HashMap;
use std::io::Write;
use std::sync::{Arc, Mutex};
use std::{
io::Read,
net::{Shutdown, SocketAddr, TcpListener, TcpStream},
sync::mpsc::{channel, Sender},
thread::{self},
};
const BUFFER_SIZE: usize = 1024;
fn main() {
let listener = TcpListener::bind("127.0.0.1:42069").unwrap();
let sockets: Arc<Mutex<HashMap<u16, TcpStream>>> = Arc::new(Mutex::new(HashMap::new()));
let c_sockets = Arc::clone(&sockets);
let cc_sockets = Arc::clone(&sockets);
let mut next_socket_id = 0;
let (tx_broadcast, rx_broadcast) = channel::<Vec<u8>>();
let (tx_disconnect, rx_disconnect) = channel::<u16>();
thread::spawn(move || loop {
let msg = &rx_broadcast.recv().unwrap();
let lock = sockets.lock().unwrap();
let sockets = lock.iter();
for (_id, mut socket) in sockets {
socket.write(msg).unwrap();
}
});
thread::spawn(move || loop {
let disconnected_socket_id = &rx_disconnect.recv().unwrap();
let mut sockets = cc_sockets.lock().unwrap();
sockets.retain(|id, _socket| id != disconnected_socket_id);
});
loop {
let (socket, addr) = listener.accept().unwrap();
let socket_id = next_socket_id;
c_sockets
.lock()
.unwrap()
.insert(socket_id, socket.try_clone().unwrap());
next_socket_id = next_socket_id + 1;
let cloned_broadcast_tx = tx_broadcast.clone();
let cloned_disconnect_tx = tx_disconnect.clone();
println!("[EVENT] {} has connected", addr);
thread::spawn(move || {
handle(
socket_id,
socket,
addr,
cloned_broadcast_tx,
cloned_disconnect_tx,
);
});
}
}
fn handle(
id: u16,
mut socket: TcpStream,
addr: SocketAddr,
sender: Sender<Vec<u8>>,
disconnect_sender: Sender<u16>,
) {
loop {
let mut buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
match socket.read(&mut buf) {
Ok(n) => {
let msg = String::from_utf8(buf[..n].to_vec())
.unwrap()
.trim()
.to_string();
if n <= 0 {
disconnect_socket(id, socket, disconnect_sender);
broadcast_message(&sender, format!("[EVENT] {id} has disconnected."));
return;
}
if msg.is_empty() {
continue;
}
broadcast_message(&sender, format!("[MESSAGE] {id}: {msg}"));
}
Err(_) => {
disconnect_socket(id, socket, disconnect_sender);
broadcast_message(&sender, format!("[EVENT] {id} has disconnected."));
return;
}
}
}
}
fn disconnect_socket(id: u16, socket: TcpStream, sender: Sender<u16>) {
sender.send(id).unwrap();
println!("[EVENT] {} has disconnected", socket.peer_addr().unwrap());
let _ = socket.shutdown(Shutdown::Both);
}
fn broadcast_message(sender: &Sender<Vec<u8>>, message: String) {
sender.send(message.as_bytes().to_vec()).unwrap();
}