Skip to content

Commit

Permalink
Unify examples to ping-pong example. Minor changes in RemoteAddr
Browse files Browse the repository at this point in the history
  • Loading branch information
lemunozm committed Feb 17, 2021
1 parent 3e20e01 commit 9643537
Show file tree
Hide file tree
Showing 21 changed files with 247 additions and 289 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Release 0.7.1
- Added WebSocket support based in `tungstenite-rs`
- Added `Network::split()` function.
- Join `udp` and `tcp` examples to make the `ping-pong` example with *WebSocket* support.
- Improved `RemoteAddr` traits.

## Release 0.7.0
- Internal improvements in order to use one thread for all adapters.
Expand Down
28 changes: 15 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ If you find a problem using the library or you have an improvement idea, do not
Managing sockets is hard because you need to fight with threads, concurrency,
IO errors that come from the OS (which are really difficult to understand in some situations),
serialization, encoding...
And if you make use of non-blocking sockets, it adds a new layer of complexity:
And if you make use of *non-blocking* sockets, it adds a new layer of complexity:
synchronize the events that come asynchronously from the OS poll.

`message-io` offers an easy way to deal with all these mentioned problems,
making them transparently for you,
the programmer that wants to make your application with its own problems.
the programmer that wants to make an application with its own problems.
For that, `message-io` offers a simple API and give only two concepts to understand:
**messages** (the data you send and receive), and **endpoints** (the recipients of that data).
This abstraction also offers the possibility to use the same API independently
Expand All @@ -36,15 +36,16 @@ You could change the protocol of your application in literally one line.
## Features
- Asynchronous: internal poll event with non-blocking sockets using [mio](https://github.com/tokio-rs/mio).
- Multiplatform: see [mio platform support](https://github.com/tokio-rs/mio#platforms).
- Multiples transports: **TCP**, **UDP** (with multicast option) and **WebSockets**.
- Multiples transports: **TCP**, **UDP** (with multicast option) and
**WebSockets** (secure and non-secure option).
- Internal encoding layer: handle messages, not data streams.
- FIFO events with timers and priority.
- Easy, intuitive and consistent API:
- Follows [KISS principle](https://en.wikipedia.org/wiki/KISS_principle).
- Abstraction from transport layer: do not think about sockets, think about messages and endpoints.
- Only two main entities to use:
- an extensible *event-queue* to manage all events synchronously,
- a *network* that manage all connections (connect, listen, remove, send, receive).
- a *network* to manage all connections (connect, listen, remove, send, receive).
- Forget concurrence problems: handle thousands of active connections and listeners without any
effort, "One thread to rule them all".
- Easy error handling.
Expand All @@ -64,9 +65,7 @@ message-io = "0.7"
- [API documentation](https://docs.rs/message-io/)
- [Basic concepts](docs/basic_concepts.md)
- [Examples](examples):
- [Basic TCP client and server](examples/tcp)
- [Basic UDP client and server](examples/udp)
- [Basic WebSocket client and server](examples/web_socket)
- [Ping Pong](examples/ping-pong) (a simple client server example)
- [Multicast](examples/multicast)
- [Distributed network with discovery server](examples/distributed)
- [File transfer](examples/file-transfer)
Expand Down Expand Up @@ -115,18 +114,21 @@ fn main() {
}
```

## Test yourself!
Clone the repository and test the TCP example that you can find in [`examples/tcp`](examples/tcp):
## Test it yourself!
Clone the repository and test the *Ping Pong* example.

Run the server:
```
cargo run --example tcp server
cargo run --example ping-pong server tcp 3456
```
In other terminals, run one or more clients:
Run the client:
```
cargo run --example tcp client <name>
cargo run --example ping-pong client tcp 127.0.0.1:3456 awesome-client
```

You can play with it changing the transport, running several clients, disconnect them, etc.
See more [here](examples/ping-pong).

## Do you need a transport protocol that `message-io` doesn't have? Add an adapter! <span id="custom-adapter"><span>

`message-io` offers two *kinds* of APIs.
Expand All @@ -143,7 +145,7 @@ If the protocol can be built in top on [`mio`](https://github.com/tokio-rs/mio#p
1. Add your *adapter* file in `src/adapters/<my-transport-protocol>.rs` that implements the
traits that you find [here](https://docs.rs/message-io/0.7.0/message_io/adapter/index.html) (only 7 mandatory functions to implement, see the [template](src/adapters/template.rs)).

1. Add a new field in the `Transport` enum found in [`src/network.rs`] to register your new adapter.
1. Add a new field in the `Transport` enum found in `src/network.rs` to register your new adapter.

That's all! You can use your new transport with the `message-io` API like any other.

Expand Down
23 changes: 23 additions & 0 deletions examples/ping-pong/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Ping-Pong example

This example shows a *clients-server* connection.
The clients send a message every second and the server responds to it.

You can run both client and server by several transports: `tcp`, `udp`, `ws` (*WebSocket*).

## Test it!

Launch the server in a terminal:
```
cargo run --example ping-pong server <transport> <port>
```

Run a client with a name (one client per terminal):
```
cargo run --example ping-pong client <transport> (<ip>:<port> | <url>) <name>
```
You can play the disconnections and reconnections using `ctrl-c` over the clients.

*Note: for *WebSocket* (`ws`), you can specify the address as usually `<ip>:<port>` or as an url:
`ws://domain:port/path` for normal websocket or `wss://domain:port/part` for secure web sockets.

51 changes: 51 additions & 0 deletions examples/ping-pong/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use super::common::{FromServerMessage, FromClientMessage};

use message_io::events::{EventQueue};
use message_io::network::{Network, NetEvent, Transport, RemoteAddr};

use std::time::{Duration};

enum Event {
Network(NetEvent<FromServerMessage>),

// This is a self event called every second.
// You can mix network events with your own events in the EventQueue.
Greet,
}

pub fn run(transport: Transport, remote_addr: RemoteAddr, name: &str) {
let mut event_queue = EventQueue::new();

let network_sender = event_queue.sender().clone();
let mut network = Network::new(move |net_event| network_sender.send(Event::Network(net_event)));

let server_id = match network.connect(Transport::Tcp, remote_addr.clone()) {
Ok(server_id) => server_id,
Err(_) => {
return println!("Can not connect to the server by {:?} to {}", transport, remote_addr)
}
};

println!("Connect to server by TCP at {}", server_id.addr());
event_queue.sender().send(Event::Greet);

loop {
match event_queue.receive() {
Event::Greet => {
let message = FromClientMessage::Greetings(format!("Hi, I am {}", name));
network.send(server_id, message);
event_queue.sender().send_with_timer(Event::Greet, Duration::from_secs(1));
}
Event::Network(net_event) => match net_event {
NetEvent::Message(_, message) => match message {
FromServerMessage::CountGreetings(text, count) => {
println!("Server says: '{}' for {} time", text, count)
}
},
NetEvent::Connected(_) => unreachable!(), // Only generated when listen
NetEvent::Disconnected(_) => return println!("Server is disconnected"),
NetEvent::DeserializationError(_) => (), // Malformed message from the server
},
}
}
}
11 changes: 11 additions & 0 deletions examples/ping-pong/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
pub enum FromClientMessage {
Greetings(String),
}

#[derive(Serialize, Deserialize)]
pub enum FromServerMessage {
CountGreetings(String, usize),
}
46 changes: 46 additions & 0 deletions examples/ping-pong/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
mod common;
mod client;
mod server;

use message_io::network::{Transport, ToRemoteAddr};

use std::net::{ToSocketAddrs};

const HELP_MSG: &str = concat!(
"Usage: ping-pong server (tcp | udp | ws) [<port>]\n",
" pong-pong client (tcp | udp | ws) (<ip>:<port>|url) <name>"
);

pub fn main() {
let args: Vec<String> = std::env::args().collect();

let transport = match args.get(2).unwrap_or(&"".into()).as_ref() {
"tcp" => Transport::Tcp,
"udp" => Transport::Udp,
"ws" => Transport::Ws,
_ => return println!("{}", HELP_MSG),
};

match args.get(1).unwrap_or(&"".into()).as_ref() {
"client" => match args.get(3) {
Some(remote_addr) => match args.get(4) {
Some(name) => {
let remote_addr = remote_addr.to_remote_addr().unwrap();
client::run(transport, remote_addr, name);
}
None => return println!("{}", HELP_MSG),
},
None => return println!("{}", HELP_MSG),
},
"server" => {
match args.get(3).unwrap_or(&"".into()).parse() {
Ok(port) => {
let addr = ("0.0.0.0", port).to_socket_addrs().unwrap().next().unwrap();
server::run(transport, addr);
}
Err(_) => return println!("{}", HELP_MSG),
};
}
_ => return println!("{}", HELP_MSG),
}
}
55 changes: 55 additions & 0 deletions examples/ping-pong/server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use super::common::{FromServerMessage, FromClientMessage};

use message_io::network::{Network, NetEvent, Endpoint, Transport};

use std::collections::{HashMap};
use std::net::{SocketAddr};

struct ClientInfo {
count: usize,
}

pub fn run(transport: Transport, addr: SocketAddr) {
let (mut network, mut event_queue) = Network::split();

let mut clients: HashMap<Endpoint, ClientInfo> = HashMap::new();

match network.listen(transport, addr) {
Ok((_resource_id, real_addr)) => println!("TCP Server running at {}", real_addr),
Err(_) => return println!("Can not listening at {}", addr),
}

loop {
match event_queue.receive() {
// Also you can use receive_timeout
NetEvent::Message(endpoint, message) => match message {
FromClientMessage::Greetings(text) => {
let mut count = clients.get_mut(&endpoint).unwrap().count;
count += 1;
println!("Client ({}) says '{}' {} times", endpoint.addr(), text, count);
let msg = format!("Hi, I hear you for {} time", count);
network.send(endpoint, FromServerMessage::CountGreetings(msg, count));
}
},
NetEvent::Connected(endpoint) => {
// Only connection oriented protocols as Tcp or Websocket will generate this event
clients.insert(endpoint, ClientInfo { count: 0 });
println!(
"Client ({}) connected (total clients: {})",
endpoint.addr(),
clients.len()
);
}
NetEvent::Disconnected(endpoint) => {
// Only connection oriented protocols as Tcp or Websocket will generate this event
clients.remove(&endpoint).unwrap();
println!(
"Client ({}) disconnected (total clients: {})",
endpoint.addr(),
clients.len()
);
}
NetEvent::DeserializationError(_) => (), // Only if the user send a malformed message.
}
}
}
15 changes: 0 additions & 15 deletions examples/tcp/README.md

This file was deleted.

47 changes: 0 additions & 47 deletions examples/tcp/client.rs

This file was deleted.

6 changes: 0 additions & 6 deletions examples/tcp/common.rs

This file was deleted.

15 changes: 0 additions & 15 deletions examples/tcp/main.rs

This file was deleted.

Loading

0 comments on commit 9643537

Please sign in to comment.