Pulling basic server functionality into development #9

Merged
michael-bailey merged 22 commits from network-client into Development 2021-04-13 17:17:58 +00:00
13 changed files with 66 additions and 542 deletions
Showing only changes of commit f72f64a18e - Show all commits

25
foundation/Cargo.toml Normal file
View File

@ -0,0 +1,25 @@
[package]
name = "foundation"
version = "0.1.0"
authors = ["Mitchell <mitchellhardie1@gmail.com>","michael-bailey <mickyb18a@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
[dependencies]
regex = "1"
crossbeam = "0.8.0"
crossbeam-channel = "0.5.0"
crossbeam-queue = "0.3.1"
parking_lot = "0.11.1"
dashmap = "4.0.2"
rayon = "1.3.1"
zeroize = "1.1.0"
crossterm = "0.19.0"
log = "0.4"
url = "2.2.0"
uuid = {version = "0.8", features = ["serde", "v4"]}
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

2
foundation/src/lib.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod prelude;
pub mod messages;

View File

@ -0,0 +1,15 @@
use serde::{Serialize, Deserialize};
/// # ClientMessage
/// This enum defined the message that a client can receive from the server
/// This uses the serde library to transform to and from json.
#[derive(Serialize, Deserialize)]
pub enum ClientStreamIn {
Disconnect {id: String},
}
pub enum ClientStreamOut {
Message {from_uuid: String},
Disconnect,
}

View File

@ -0,0 +1,2 @@
pub mod client;
pub mod network;

View File

@ -0,0 +1,14 @@
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
pub enum NetworkSockIn {
Info,
Connect {uuid: String, username: String, address: String},
}
#[derive(Serialize, Deserialize)]
pub enum NetworkSockOut<'a> {
Request,
GotInfo {server_name: &'a str, server_owner: &'a str}
}

View File

@ -0,0 +1,8 @@
pub trait IMessagable<TMessage, TSender> {
fn send_message(&self, msg: TMessage);
fn set_sender(&self, sender: TSender);
}
pub trait ICooperative {
fn tick(&self);
}

View File

@ -1,195 +0,0 @@
// pub mod client_profile;
// pub mod client_v3;
pub mod traits;
use std::cmp::Ordering;
use std::net::TcpStream;
use std::sync::Mutex;
use std::sync::Arc;
use std::io::{BufReader, BufWriter};
use std::io::BufRead;
use uuid::Uuid;
use serde::{Serialize, Deserialize};
use crossbeam_channel::{Sender, Receiver, unbounded};
use traits::IClient;
use crate::lib::foundation::{ICooperative, IMessagable};
use crate::lib::server::ServerMessages;
/// # ClientMessage
/// This enum defined the message that a client can receive from the server
/// This uses the serde library to transform to and from json.
#[derive(Serialize, Deserialize)]
pub enum ClientMessage {
Disconnect {id: String},
Update {id: String},
ServerMessage {id: String, msg: String},
NewMessage {id: String, from_user_id: String, msg: String},
NewgroupMessage {id: String, from_group_id: String, from_user_id: String, msg: String},
}
/// # ClientSocketMessage
/// This enum defines a message that can be sent from a client to the server once connected
/// This uses the serde library to transform to and from json.
#[derive(Serialize, Deserialize)]
pub enum ClientSocketMessage {
Disconnect {id: String},
SendMessage {id: String, to_user_id: String, msg: String}
}
/// # Client
/// This struct represents a connected user.
///
/// ## Attrubutes
/// - uuid: The id of the connected user.
/// - username: The username of the connected user.
/// - address: The the address of the connected client.
///
/// - stream: The socket for the connected client.
/// - owner: An optional reference to the owning object.
#[derive(Debug, Serialize)]
pub struct Client {
pub uuid: Uuid,
username: String,
address: String,
// non serializable
#[serde(skip)]
server_channel: Option<Sender<ServerMessages>>,
#[serde(skip)]
input: Sender<ClientMessage>,
#[serde(skip)]
output: Receiver<ClientMessage>,
#[serde(skip)]
stream: Mutex<Option<TcpStream>>,
#[serde(skip)]
stream_reader: Mutex<Option<BufReader<TcpStream>>>,
#[serde(skip)]
stream_writer: Mutex<Option<BufWriter<TcpStream>>>,
}
// client funciton implmentations
impl IClient<ClientMessage> for Client {
fn new(
uuid: String,
username: String,
address: String,
stream: TcpStream,
server_channel: Sender<ServerMessages>
) -> Arc<Client> {
let (sender, receiver) = unbounded();
let out_stream = stream.try_clone().unwrap();
let in_stream = stream.try_clone().unwrap();
Arc::new(Client {
username,
uuid: Uuid::parse_str(&uuid).expect("invalid id"),
address,
server_channel: Some(server_channel),
input: sender,
output: receiver,
stream: Mutex::new(Some(stream)),
stream_reader: Mutex::new(Some(BufReader::new(in_stream))),
stream_writer: Mutex::new(Some(BufWriter::new(out_stream))),
})
}
// MARK: - removeable
fn send(&self, _bytes: Vec<u8>) -> Result<(), &str> { todo!() }
fn recv(&self) -> Option<Vec<u8>> { todo!() }
// Mark: end -
}
impl IMessagable<ClientMessage> for Client{
fn send_message(&self, msg: ClientMessage) {
self.input.send(msg).expect("failed to send message to client.");
}
}
// cooperative multitasking implementation
impl ICooperative for Client {
fn tick(&self) {
// aquire locks (so value isn't dropped)
let mut reader_lock = self.stream_reader.lock().unwrap();
let mut writer_lock = self.stream_writer.lock().unwrap();
// aquiring mutable buffers
let reader = reader_lock.as_mut().unwrap();
let _writer = writer_lock.as_mut().unwrap();
// create buffer
let mut buffer = String::new();
// loop over all lines that have been sent.
while let Ok(_size) = reader.read_line(&mut buffer) {
let command = serde_json::from_str::<ClientSocketMessage>(buffer.as_str()).unwrap();
match command {
ClientSocketMessage::Disconnect {id} => println!("got Disconnect from id: {:?}", id),
_ => println!("New command found"),
}
}
// handle incomming messages
}
}
// default value implementation
impl Default for Client {
fn default() -> Self {
let (sender, reciever) = unbounded();
Client {
username: "generic_client".to_string(),
uuid: Uuid::new_v4(),
address: "127.0.0.1".to_string(),
output: reciever,
input: sender,
server_channel: None,
stream: Mutex::new(None),
stream_reader: Mutex::new(None),
stream_writer: Mutex::new(None),
}
}
}
// MARK: - used for sorting.
impl PartialEq for Client {
fn eq(&self, other: &Self) -> bool {
self.uuid == other.uuid
}
}
impl Eq for Client {
}
impl PartialOrd for Client {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Client {
fn cmp(&self, other: &Self) -> Ordering {
self.uuid.cmp(&other.uuid)
}
}

View File

@ -1,29 +0,0 @@
use std::sync::Arc;
use std::net::TcpStream;
use crossbeam_channel::Sender;
use crate::lib::server::ServerMessages;
/// # TClient
/// This trait represents the methods that a client must implement
/// in order to be used with a client manager
///
/// # Methods
/// - new: creates a new client from an id, username and a address.
/// - send: send a message to the client.
/// - recv: if there is a message in the queue, returns the message
/// - send_msg: sends a event message to the client
/// - recv_msg: used by the client to receive and process event messages
pub trait IClient<TClientMessage> {
fn new(
uuid: String,
username: String,
address: String,
stream: TcpStream,
server_channel: Sender<ServerMessages>
) -> Arc<Self>;
fn send(&self, bytes: Vec<u8>) -> Result<(), &str>;
fn recv(&self) -> Option<Vec<u8>>;
}

View File

@ -1,116 +0,0 @@
pub mod client;
pub mod traits;
// use crate::lib::server::ServerMessages;
use std::sync::Arc;
use std::sync::Mutex;
use std::collections::HashMap;
use crossbeam_channel::{unbounded, Receiver, Sender};
use uuid::Uuid;
use self::client::Client;
use self::client::ClientMessage;
use self::traits::TClientManager;
use crate::lib::server::ServerMessages;
use crate::lib::foundation::IMessagable;
use crate::lib::foundation::ICooperative;
enum ClientManagerMessages {
#[allow(dead_code)]
DropAll,
#[allow(dead_code)]
MessageClient,
}
/// # ClientManager
/// This struct manages all connected users
#[derive(Debug)]
pub struct ClientManager {
clients: Mutex<HashMap<Uuid, Arc<Client>>>,
server_channel: Sender<ServerMessages>,
sender: Sender<ClientManagerMessages>,
receiver: Receiver<ClientManagerMessages>,
}
impl ClientManager {
pub fn new(server_channel: Sender<ServerMessages>) -> Arc<Self> {
let (sender, receiver) = unbounded();
Arc::new(ClientManager {
clients: Mutex::default(),
server_channel,
sender,
receiver,
})
}
}
impl TClientManager<Client, ClientMessage> for ClientManager {
fn add_client(&self, client: std::sync::Arc<Client>) {
self.clients.lock().unwrap().insert(client.uuid, client);
}
fn remove_client(&self, uuid: Uuid) {
let _ = self.clients.lock().unwrap().remove(&uuid);
}
fn send_message_to_client(&self, uuid: Uuid, msg: ClientMessage) {
let clients = self.clients.lock().unwrap();
let client = clients.get(&uuid).unwrap();
client.send_message(msg);
}
}
impl ICooperative for ClientManager {
fn tick(&self) {
for message in self.receiver.iter() {
match message {
ClientManagerMessages::DropAll => {
println!("cannot drop all clients yet")
}
_ => println!("[Client Manager]: method not implemented")
}
}
// allocate time for clients.
let clients = self.clients.lock().unwrap();
let _ = clients.iter().map(|(_uuid, client)| client.tick());
}
}
#[cfg(test)]
mod test {
// use super::ClientManager;
// use std::sync::Arc;
// use crate::lib::Foundation::{IOwner};
#[test]
fn test_get_ref() {
// let client_manager = ClientManager::new();
// let _cm_ref = client_manager.get_ref();
// assert_eq!(Arc::weak_count(&client_manager), 2);
}
#[test]
fn test_add_client() {
todo!()
}
#[test]
fn test_remove_client() {
todo!()
}
#[test]
fn test_remove_all_clients() {
todo!()
}
}

View File

@ -1,13 +0,0 @@
use crate::lib::server::client_management::client::ClientMessage;
use std::sync::Arc;
use uuid::Uuid;
/**
* @michael-bailey
*/
pub trait TClientManager<TClient,TClientMessage> {
fn add_client(&self, client: Arc<TClient>);
fn remove_client(&self, uuid: Uuid);
fn send_message_to_client(&self, uuid: Uuid, msg: ClientMessage);
}

View File

@ -1,7 +0,0 @@
pub trait IMessagable<M> {
fn send_message(&self, msg: M);
}
pub trait ICooperative {
fn tick(&self);
}

View File

@ -1,117 +0,0 @@
use crate::lib::server::Client;
use std::net::TcpListener;
use std::sync::Arc;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Write;
use std::io::BufRead;
use serde::{Deserialize, Serialize};
use crossbeam_channel::Sender;
use crate::lib::server::ServerMessages;
use crate::lib::foundation::ICooperative;
use crate::lib::server::client_management::client::traits::IClient;
/// # NetworkSockIn
/// these messages can be sent by a client on connecting
#[derive(Serialize, Deserialize)]
enum NetworkSockIn {
Info,
Connect {uuid: String, username: String, address: String},
}
/// # NetworkSockOut
/// these messages are sent by the network manager on connecting and requesting
#[derive(Serialize, Deserialize)]
enum NetworkSockOut<'a> {
Request,
GotInfo {server_name: &'a str, server_owner: &'a str}
}
// these are control signals from the server.
// pub enum NetworkMessages {
// }
pub struct NetworkManager {
listener: TcpListener,
server_channel: Sender<ServerMessages>,
}
impl NetworkManager {
pub fn new(
port: String,
server_channel: Sender<ServerMessages>
) -> Arc<NetworkManager> {
let mut address = "0.0.0.0:".to_string();
address.push_str(&port);
let listener = TcpListener::bind(address)
.expect("Could not bind to address");
Arc::new(NetworkManager {
listener,
server_channel,
})
}
}
impl ICooperative for NetworkManager {
fn tick(&self) {
// get all new connections
// handle each request
for connection in self.listener.incoming() {
if let Ok(stream) = connection {
// create buffered writers
let mut reader = BufReader::new(stream.try_clone().unwrap());
let mut writer = BufWriter::new(stream.try_clone().unwrap());
let mut buffer = String::new();
// request is always sent on new connection
writer.write_all(
serde_json::to_string(&NetworkSockOut::Request).unwrap().as_bytes()
).unwrap_or_default();
writer.write_all(b"\n").unwrap_or_default();
writer.flush().unwrap_or_default();
// read the new request into a buffer
let res = reader.read_line(&mut buffer);
if res.is_err() {continue;}
// turn into enum for pattern matching
if let Ok(request) = serde_json::from_str::<NetworkSockIn>(&buffer) {
// perform action based on the enum
match request {
NetworkSockIn::Info => {
writer.write_all(
serde_json::to_string(
&NetworkSockOut::GotInfo {
server_name: "oof",
server_owner: "michael"
}
).unwrap().as_bytes()
).unwrap();
writer.flush().unwrap();
}
NetworkSockIn::Connect { uuid, username, address } => {
let new_client = Client::new(
uuid,
username,
address,
stream.try_clone().unwrap(),
self.server_channel.clone()
);
self.server_channel.send(
ServerMessages::ClientConnected(new_client)
).unwrap_or_default();
}
}
}
}
}
}
}

View File

@ -1,65 +0,0 @@
pub mod client_management;
pub mod network_manager;
use uuid::Uuid;
use crate::lib::server::network_manager::NetworkManager;
use std::sync::Arc;
use crossbeam_channel::{Receiver, unbounded};
use crate::lib::server::client_management::ClientManager;
use crate::lib::server::client_management::traits::TClientManager;
use crate::lib::foundation::{ICooperative};
use client_management::client::Client;
/// # ServerMessages
/// This is used internally
#[derive(Debug)]
pub enum ServerMessages {
ClientConnected(Arc<Client>),
#[allow(dead_code)]
ClientDisconnected(Uuid),
}
pub struct Server {
client_manager: Arc<ClientManager>,
network_manager: Arc<NetworkManager>,
receiver: Receiver<ServerMessages>,
}
impl Server {
pub fn new() -> Arc<Server> {
let (sender, receiver) = unbounded();
Arc::new(Server {
client_manager: ClientManager::new(sender.clone()),
network_manager: NetworkManager::new("5600".to_string(), sender.clone()),
receiver,
})
}
}
impl ICooperative for Server{
fn tick(&self) {
// handle new messages loop
for message in self.receiver.try_iter() {
match message {
ServerMessages::ClientConnected(client) => {
self.client_manager.add_client(client);
},
ServerMessages::ClientDisconnected(uuid) => {
self.client_manager.remove_client(uuid);
}
}
}
// alocate time for other components
self.network_manager.tick();
self.client_manager.tick();
}
}