simple/
simple.rs

1use spunet::{
2    Protocol,
3    client::{
4        Client,
5        ClientError,
6    },
7    server::{
8        Server,
9        ServerError,
10    },
11};
12use std::net::{
13    SocketAddr,
14    IpAddr,
15    Ipv4Addr,
16};
17use std::time::Duration;
18use std::io::{ self, Write };
19
20// Server address stuff.
21const SERVER_IP: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
22const SERVER_PORT: u16 = 6969;
23const SERVER_ADDRESS: SocketAddr = SocketAddr::new(SERVER_IP, SERVER_PORT);
24
25// Buffer size for received messages.
26const BUFFER_SIZE: usize = 1024;
27
28// Timeout for reading messages on the receiving thread
29// (this library runs parallel threads for reading which is a blocking operation).
30// This should be set to something small if you want to receive
31// messages quickly, but it shouldn't be zero since that
32// doesn't even give the receiver a chance to receive.
33const READ_TIMEOUT: Duration = Duration::from_nanos(1);
34
35// Optional timeout for when a client is connecting to a server.
36const CONNECTION_TIMEOUT: Option<Duration> = None;
37
38fn main() {
39    let args: Vec<String> = std::env::args().collect();
40
41    if args.len() == 2 {
42        let protocol = match input("Protocol [tcp/udp]: ").to_lowercase().as_str() {
43            "tcp" => Protocol::TCP,
44            "udp" => Protocol::UDP,
45            _ => {
46                eprintln!("Invalid protocol! Please choose either TCP or UDP!");
47
48                std::process::exit(1);
49            },
50        };
51
52        match args[1].as_str() {
53            "server" => spawn_server(protocol).unwrap(),
54            "client" => spawn_client(protocol).unwrap(),
55            _ => {
56                eprintln!("Invalid option: {}", args[1]);
57
58                std::process::exit(1);
59            },
60        };
61    }
62
63    else {
64        eprintln!("Usage: {} <COMMAND>", args[0]);
65        eprintln!("");
66        eprintln!("Commands:");
67        eprintln!("  server ... Spawn the server.");
68        eprintln!("  client ... Spawn a client and connect to the server.");
69
70        std::process::exit(1);
71    }
72}
73
74fn input(prefix: &str) -> String {
75    print!("{}", prefix);
76
77    io::stdout().flush().unwrap();
78
79    let mut user_input = String::new();
80
81    io::stdin().read_line(&mut user_input).unwrap();
82
83    return user_input.trim().to_string();
84}
85
86// This function is what spawns the server for this example.
87fn spawn_server(protocol: Protocol) -> Result<(), ServerError> {
88    println!("Starting server...");
89
90    // Creating the server and binding it to a local address to listen on.
91    let mut server: Server<BUFFER_SIZE> = Server::bind(SERVER_ADDRESS, READ_TIMEOUT)?;
92
93    // Server loop.
94    loop {
95        // Check to see if the server is still running.
96        if server.is_running() == false {
97            break;
98        }
99
100        // Flush the server.
101        //
102        // I just now, as I was writing this comment, realized
103        // that you are flushing using a handle to the underlying
104        // TCP stream! Ha! Flushing... using a handle! :D
105        server.flush()?;
106
107        // There is a separate thread that accepts incoming client
108        // connections, and this function simply gets a list of
109        // any client IDs for clients that joined since the last call
110        // of this function. If this function was never called, it
111        // will return all clients, which means this function is guarenteed
112        // to return all client IDs through out the runtime of the program.
113        let new_clients = server.new_clients()?;
114
115        // Just iterating through all the new clients and welcoming them.
116        for client_id in new_clients {
117            // This should always return Some(...) since the client ID
118            // is guarenteed to be a valid client's ID.
119            let client_info = server.get_client_info(client_id).unwrap();
120
121            println!("New client {}: {}", client_id.id, client_info.client_address);
122            server.send(
123                client_id,
124                protocol,
125                format!(
126                    "Hello client #{}! Your address is: {}",
127                    client_id.id,
128                    client_info.client_address,
129                ).as_bytes(),
130            )?;
131        }
132
133        // Iterate through every client connected to the server.
134        for client_id in server.clients() {
135            // If this returns false, this is most likely because the
136            // server kicked this client in a previous iteration.
137            if server.is_valid_client(client_id) == false {
138                println!("Invalid client, skipping... (#{})", client_id.id);
139
140                continue;
141            }
142
143            // Unlike the vanilla implementations of TCP and UDP in the standard libraries
144            // of programming languages, this library's recv() function is non-blocking,
145            // so it returns either data or nothing.
146            // The read timeout (discussed earlier) is for the blocking recv() call in the
147            // reading thread so the reading thread doesn't get stuck.
148            if let Some((message_raw, message_size)) = server.recv(client_id, protocol)? {
149                // The reason that a slice of the raw bytes as opposed to the whole
150                // thing is being used is because the string will actually have a lot
151                // of termination characters at the end since the raw bytes message
152                // is the same size as the BUFFER_SIZE constant. So by passing a slice,
153                // this string will only be made with bytes that were received as opposed
154                // to the entire buffer.
155                let message = String::from_utf8_lossy(&message_raw[..message_size]).to_string();
156
157                println!("<Client #{}> {}", client_id.id, message);
158
159                match message.as_str() {
160                    "quit" => {
161                        println!("Kicking client #{}...", client_id.id);
162                        server.kick(client_id)?;
163                    },
164                    "stop" => {
165                        println!("Stopping server...");
166                        server.stop()?;
167                    },
168                    "protocol" => {
169                        println!("Protocol: {:?}", protocol);
170                    },
171                    _ => (),
172                };
173            }
174        }
175    }
176
177    Ok(())
178}
179
180// This function is what spawns a client for this example.
181fn spawn_client(protocol: Protocol) -> Result<(), ClientError> {
182    println!("Creating a client...");
183
184    // Pretty self-explanatory, just creating a client. :)
185    let mut client: Client<BUFFER_SIZE> = Client::new(READ_TIMEOUT);
186
187    println!("Connecting to server... ({})", SERVER_ADDRESS);
188    client.connect(SERVER_ADDRESS, CONNECTION_TIMEOUT)?;
189    client.send(protocol, "Hello, server!".as_bytes())?;
190    client.flush()?; // Send the message immediately by flushing the client.
191
192    loop {
193        // Check whether or not the client is still connected.
194        if client.is_connected() == false {
195            break;
196        }
197
198        // Flush the client.
199        client.flush()?;
200
201        // Pretty much the same as the server example.
202        if let Some((message_raw, message_size)) = client.recv(protocol)? {
203            let message = String::from_utf8_lossy(&message_raw[..message_size]).to_string();
204
205            println!("<Server> {}", message);
206        }
207
208        let user_message = input("[ Message To Server ]: ");
209
210        client.send(protocol, user_message.as_bytes())?;
211
212        match user_message.as_str() {
213            "quit" => {
214                client.disconnect()?; // This needs to be handled client-side too,
215                                      // since TCP and UDP (vanilla implementations)
216                                      // do not let the clients know that they have
217                                      // been disconnected. Clients also don't let
218                                      // the server know that they have disconnected.
219            },
220            "stop" => {
221                println!("Sent stop request to server...");
222                client.disconnect()?; // Same reason as the above comment. ^
223            },
224            "protocol" => {
225                println!("Protocol: {:?}", protocol);
226            },
227            _ => {
228                if let Ok(times) = user_message.parse::<usize>() {
229                    for _ in 0..times {
230                        client.send(protocol, "Ping!".as_bytes())?;
231                    }
232                }
233            },
234        };
235    }
236
237    Ok(())
238}