init
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/target
|
||||||
|
/client/target
|
||||||
|
/server/target
|
||||||
7
client/Cargo.lock
generated
Normal file
7
client/Cargo.lock
generated
Normal 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
8
client/Cargo.toml
Normal 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
70
client/src/main.rs
Normal 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
7
server/Cargo.lock
generated
Normal 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
8
server/Cargo.toml
Normal 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
111
server/src/main.rs
Normal 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();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user