From 2f739c3118adbb176bf14470114f84d14a77f28b Mon Sep 17 00:00:00 2001 From: FyloZ Date: Wed, 22 Mar 2023 23:27:53 -0400 Subject: [PATCH] Serialization :) --- Cargo.lock | 4 +++ client/Cargo.toml | 1 + client/src/main.rs | 31 +++++++++++------------ messages/src/client_registration.rs | 36 +++++++++++++++++++------- messages/src/lib.rs | 20 ++++++++++++--- server/Cargo.toml | 1 + server/src/client.rs | 22 ++++++++++++++++ server/src/epoll.rs | 8 +++--- server/src/main.rs | 18 +------------ server/src/tcp_server.rs | 39 ++++++++++++++++++----------- 10 files changed, 116 insertions(+), 64 deletions(-) create mode 100644 server/src/client.rs diff --git a/Cargo.lock b/Cargo.lock index 5ba3082..a98c398 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,6 +5,9 @@ version = 3 [[package]] name = "client" version = "0.1.0" +dependencies = [ + "messages", +] [[package]] name = "libc" @@ -21,4 +24,5 @@ name = "server" version = "0.1.0" dependencies = [ "libc", + "messages", ] diff --git a/client/Cargo.toml b/client/Cargo.toml index 729587b..f153f78 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +messages = { path = "../messages" } \ No newline at end of file diff --git a/client/src/main.rs b/client/src/main.rs index 69010c0..af27585 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -1,25 +1,24 @@ use std::io; -use std::net::{SocketAddr, UdpSocket}; -use std::thread::sleep; -use std::time::Duration; +use std::io::Write; +use std::net::{SocketAddr, TcpStream}; + +use messages::{any_as_u8_slice, Serializable}; +use messages::client_registration::ClientRegistration; fn main() -> io::Result<()> { - let bind_addr = SocketAddr::from(([127, 0, 0, 1], 4432)); - let server_addr = SocketAddr::from(([127, 0, 0, 1], 4433)); + let addr = SocketAddr::from(([127, 0, 0, 1], 4433)); + let mut stream = TcpStream::connect(addr)?; - let socket = UdpSocket::bind(bind_addr)?; - socket.connect(server_addr)?; + let registration = ClientRegistration { + major_version: 0, + minor_version: 0, + name: "My new client :)".to_string(), + }; - let mut buffer: [u8; 128] = [0; 128]; - for i in 1..10 { - for j in 1..128 { - buffer[j] = j as u8; - } + let mut buf = [0u8; 1024]; + registration.serialize(&mut buf); - socket.send(&buffer)?; - println!("{}", i); - sleep(Duration::from_millis(1000)); - } + stream.write(&buf)?; Ok(()) } diff --git a/messages/src/client_registration.rs b/messages/src/client_registration.rs index f602842..4e9e2ee 100644 --- a/messages/src/client_registration.rs +++ b/messages/src/client_registration.rs @@ -1,19 +1,37 @@ -use crate::Serializable; +use crate::{DeserializationError, Serializable}; -struct ClientRegistration { - name: str +const CR_BUFFER_MIN_CAPACITY: usize = 3; + +// Contains the version using semantic versioning +// The patch version is omitted, because it should not affect the communication between the server and the client +// Therefore, a client and a server with a different patch version should be completely compatible +pub struct ClientRegistration { + pub major_version: u8, + pub minor_version: u8, + pub name: String, } impl Serializable for ClientRegistration { - fn serialize(&self) -> Vec { - let mut buf: Vec = Vec::new(); + fn serialize(&self, buf: &mut [u8]) { + let mut vec_buf: Vec = Vec::with_capacity(buf.len()); + vec_buf.insert(0, self.major_version); + vec_buf.insert(1, self.minor_version); - buf.extend_from_slice(self.name.as_bytes()); + let name_bytes = self.name.as_bytes(); + vec_buf.extend_from_slice(name_bytes); - buf + buf[..vec_buf.len()].copy_from_slice(&vec_buf); } - fn deserialize(buf: &[u8]) -> Self { - String::from_utf8() + fn deserialize(buf: &[u8]) -> Result { + if buf.len() < CR_BUFFER_MIN_CAPACITY { + return Err(DeserializationError::MissingData); + } + + Ok(ClientRegistration { + major_version: buf[0], + minor_version: buf[1], + name: String::from_utf8_lossy(&buf[2..]).into_owned(), + }) } } diff --git a/messages/src/lib.rs b/messages/src/lib.rs index 34e5a80..c0873f4 100644 --- a/messages/src/lib.rs +++ b/messages/src/lib.rs @@ -1,6 +1,18 @@ -mod client_registration; +pub mod client_registration; -trait Serializable { - fn serialize(&self) -> Vec; - fn deserialize(buf: Vec) -> Self; +pub trait Serializable where Self: Sized { + fn serialize(&self, buf: &mut [u8]); + fn deserialize(buf: &[u8]) -> Result; +} + +// From: https://stackoverflow.com/questions/28127165/how-to-convert-struct-to-u8 +pub unsafe fn any_as_u8_slice(p: &T) -> &[u8] { + ::core::slice::from_raw_parts( + (p as *const T) as *const u8, + ::core::mem::size_of::(), + ) +} + +pub enum DeserializationError { + MissingData } diff --git a/server/Cargo.toml b/server/Cargo.toml index ccf6525..6b9d405 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -5,3 +5,4 @@ edition = "2021" [dependencies] libc = "0.2.140" +messages = { path = "../messages" } \ No newline at end of file diff --git a/server/src/client.rs b/server/src/client.rs new file mode 100644 index 0000000..0991818 --- /dev/null +++ b/server/src/client.rs @@ -0,0 +1,22 @@ +use std::net::TcpStream; + +static mut NEXT_CLIENT_ID: u8 = 0; + +pub struct Client { + pub id: u8, + pub stream: TcpStream +} + +impl Client { + pub fn new(stream: TcpStream) -> Self { + unsafe { + let id = NEXT_CLIENT_ID; + NEXT_CLIENT_ID += 1; + + Client { + id, + stream, + } + } + } +} \ No newline at end of file diff --git a/server/src/epoll.rs b/server/src/epoll.rs index 10a29f3..ff22f86 100644 --- a/server/src/epoll.rs +++ b/server/src/epoll.rs @@ -26,11 +26,11 @@ impl Epoll { } } - pub fn add_read_interest(&self, fd: RawFd, key: u64) -> io::Result<()> { + pub fn add_read_interest(&self, fd: RawFd, key: u16) -> io::Result<()> { add_interest(self.fd, fd, listener_read_event(key)) } - pub fn modify_read_interest(&self, fd: RawFd, key: u64) -> io::Result<()> { + pub fn modify_read_interest(&self, fd: RawFd, key: u16) -> io::Result<()> { modify_interest(self.fd, fd, listener_read_event(key)) } @@ -97,9 +97,9 @@ fn modify_interest(epoll_fd: RawFd, fd: RawFd, mut event: epoll_event) -> io::Re Ok(()) } -fn listener_read_event(key: u64) -> epoll_event { +fn listener_read_event(key: u16) -> epoll_event { epoll_event { events: READ_FLAGS as u32, - u64: key, + u64: key as u64, } } diff --git a/server/src/main.rs b/server/src/main.rs index 51f254e..670d717 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -5,23 +5,7 @@ use crate::tcp_server::TcpServer; mod epoll; mod tcp_server; - -#[derive(Debug)] -pub struct RequestContext { - pub stream: TcpStream, - pub content_length: usize, - pub buf: Vec, -} - -impl RequestContext { - fn new(stream: TcpStream) -> Self { - Self { - stream, - buf: Vec::new(), - content_length: 0, - } - } -} +mod client; fn main() -> io::Result<()> { let addr = SocketAddr::from(([127, 0, 0, 1], 4433)); diff --git a/server/src/tcp_server.rs b/server/src/tcp_server.rs index ae5aae6..aadda79 100644 --- a/server/src/tcp_server.rs +++ b/server/src/tcp_server.rs @@ -1,20 +1,24 @@ use std::collections::HashMap; use std::io; +use std::io::Read; use std::net::{SocketAddr, TcpListener}; use std::os::fd::{AsRawFd, RawFd}; +use messages::client_registration::ClientRegistration; +use messages::Serializable; +use crate::client::Client; use crate::epoll::{Epoll, is_read_event, is_write_event}; -use crate::RequestContext; -const KEY_NEW_CONNECTION: u64 = 100; +// Based on: https://www.zupzup.org/epoll-with-rust/index.html + +const KEY_NEW_CONNECTION: u16 = u16::MAX; pub struct TcpServer { addr: SocketAddr, listener: TcpListener, listener_fd: RawFd, epoll: Epoll, - request_contexts: HashMap, - key: u64, + request_contexts: HashMap, } impl TcpServer { @@ -33,7 +37,6 @@ impl TcpServer { listener_fd, epoll, request_contexts: HashMap::new(), - key: KEY_NEW_CONNECTION, }) } @@ -48,9 +51,9 @@ impl TcpServer { .collect::>(); for (events, u64) in events { - match *u64 { + match *u64 as u16 { KEY_NEW_CONNECTION => self.accept_connection()?, - key => self.handle_event(*events, key) + key => self.handle_event(*events, key as u8) } } } @@ -60,10 +63,13 @@ impl TcpServer { match self.listener.accept() { Ok((stream, addr)) => { stream.set_nonblocking(true)?; - println!("New client: {addr}"); - self.key += 1; - self.epoll.add_read_interest(stream.as_raw_fd(), self.key)?; - self.request_contexts.insert(self.key, RequestContext::new(stream)); + + let fd = stream.as_raw_fd(); + let client = Client::new(stream); + println!("New client: {addr} ({})", client.id); + + self.epoll.add_read_interest(fd, client.id as u16)?; + self.request_contexts.insert(client.id, client); } Err(e) => eprintln!("Couldn't accept: {e}") }; @@ -71,14 +77,19 @@ impl TcpServer { self.epoll.modify_read_interest(self.listener_fd, KEY_NEW_CONNECTION) } - fn handle_event(&mut self, events: u32, key: u64) { + fn handle_event(&mut self, events: u32, key: u8) { let mut to_delete = None; - if let Some(context) = self.request_contexts.get_mut(&key) { + if let Some(client) = self.request_contexts.get_mut(&key) { match events { v if is_read_event(v) => { - // context.read_cb(key, epoll_fd)?; + let mut buf = [0u8; 1024]; + let read_length = client.stream.read(&mut buf).expect("Failed to read stream"); + let registration = ClientRegistration::deserialize(&buf[..read_length]); + + println!("Test"); } v if is_write_event(v) => { + println!("Write Event"); // context.write_cb(key, epoll_fd)?; to_delete = Some(key); }