mirror of
https://github.com/tokio-rs/tokio.git
synced 2025-09-25 12:00:35 +00:00

In an effort to reach API stability, the `tokio` crate is shedding its _public_ dependencies on crates that are either a) do not provide a stable (1.0+) release with longevity guarantees or b) match the `tokio` release cadence. Of course, implementing `std` traits fits the requirements. The on exception, for now, is the `Stream` trait found in `futures_core`. It is expected that this trait will not change much and be moved into `std. Since Tokio is not yet going reaching 1.0, I feel that it is acceptable to maintain a dependency on this trait given how foundational it is. Since the `Stream` implementation is optional, types that are logically streams provide `async fn next_*` functions to obtain the next value. Avoiding the `next()` name prevents fn conflicts with `StreamExt::next()`. Additionally, some misc cleanup is also done: - `tokio::io::io` -> `tokio::io::util`. - `delay` -> `delay_until`. - `Timeout::new` -> `timeout(...)`. - `signal::ctrl_c()` returns a future instead of a stream. - `{tcp,unix}::Incoming` is removed (due to lack of `Stream` trait). - `time::Throttle` is removed (due to lack of `Stream` trait). - Fix: `mpsc::UnboundedSender::send(&self)` (no more conflict with `Sink` fns).
105 lines
3.7 KiB
Rust
105 lines
3.7 KiB
Rust
//! A "print-each-packet" server with Tokio
|
|
//!
|
|
//! This server will create a TCP listener, accept connections in a loop, and
|
|
//! put down in the stdout everything that's read off of each TCP connection.
|
|
//!
|
|
//! Because the Tokio runtime uses a thread pool, each TCP connection is
|
|
//! processed concurrently with all other TCP connections across multiple
|
|
//! threads.
|
|
//!
|
|
//! To see this server in action, you can run this in one terminal:
|
|
//!
|
|
//! cargo run --example print\_each\_packet
|
|
//!
|
|
//! and in another terminal you can run:
|
|
//!
|
|
//! cargo run --example connect 127.0.0.1:8080
|
|
//!
|
|
//! Each line you type in to the `connect` terminal should be written to terminal!
|
|
//!
|
|
//! Minimal js example:
|
|
//!
|
|
//! ```js
|
|
//! var net = require("net");
|
|
//!
|
|
//! var listenPort = 8080;
|
|
//!
|
|
//! var server = net.createServer(function (socket) {
|
|
//! socket.on("data", function (bytes) {
|
|
//! console.log("bytes", bytes);
|
|
//! });
|
|
//!
|
|
//! socket.on("end", function() {
|
|
//! console.log("Socket received FIN packet and closed connection");
|
|
//! });
|
|
//! socket.on("error", function (error) {
|
|
//! console.log("Socket closed with error", error);
|
|
//! });
|
|
//!
|
|
//! socket.on("close", function (with_error) {
|
|
//! if (with_error) {
|
|
//! console.log("Socket closed with result: Err(SomeError)");
|
|
//! } else {
|
|
//! console.log("Socket closed with result: Ok(())");
|
|
//! }
|
|
//! });
|
|
//!
|
|
//! });
|
|
//!
|
|
//! server.listen(listenPort);
|
|
//!
|
|
//! console.log("Listening on:", listenPort);
|
|
//! ```
|
|
//!
|
|
|
|
#![warn(rust_2018_idioms)]
|
|
|
|
use tokio::net::TcpListener;
|
|
use tokio_util::codec::{BytesCodec, Decoder};
|
|
|
|
use futures::StreamExt;
|
|
use std::env;
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
// Allow passing an address to listen on as the first argument of this
|
|
// program, but otherwise we'll just set up our TCP listener on
|
|
// 127.0.0.1:8080 for connections.
|
|
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
|
|
|
// Next up we create a TCP listener which will listen for incoming
|
|
// connections. This TCP listener is bound to the address we determined
|
|
// above and must be associated with an event loop, so we pass in a handle
|
|
// to our event loop. After the socket's created we inform that we're ready
|
|
// to go and start accepting connections.
|
|
let mut listener = TcpListener::bind(&addr).await?;
|
|
println!("Listening on: {}", addr);
|
|
|
|
loop {
|
|
// Asynchronously wait for an inbound socket.
|
|
let (socket, _) = listener.accept().await?;
|
|
|
|
// And this is where much of the magic of this server happens. We
|
|
// crucially want all clients to make progress concurrently, rather than
|
|
// blocking one on completion of another. To achieve this we use the
|
|
// `tokio::spawn` function to execute the work in the background.
|
|
//
|
|
// Essentially here we're executing a new task to run concurrently,
|
|
// which will allow all of our clients to be processed concurrently.
|
|
tokio::spawn(async move {
|
|
// We're parsing each socket with the `BytesCodec` included in `tokio::codec`.
|
|
let mut framed = BytesCodec::new().framed(socket);
|
|
|
|
// We loop while there are messages coming from the Stream `framed`.
|
|
// The stream will return None once the client disconnects.
|
|
while let Some(message) = framed.next().await {
|
|
match message {
|
|
Ok(bytes) => println!("bytes: {:?}", bytes),
|
|
Err(err) => println!("Socket closed with error: {:?}", err),
|
|
}
|
|
}
|
|
println!("Socket received FIN packet and closed connection");
|
|
});
|
|
}
|
|
}
|