原本想直接学习Actix-Web,一层层看下来,发现内容太多,杂乱,没有目的。所以,一层层剥下来,最终落脚到Tokio上,现在先了解Tokio的相关内容,最终学习Actix相关内容。
Tokio是Rust的一个异步编程库,它是一个事件驱动,使用非阻塞IO编写异步程序的库。总的来说,有几个特点:
其他特色:
imp drop
来定制清理工作。The map combinator takes a future and returns a new future that applies a function to the value yielded by the first future.
map接收一个future然后返回一个新的future。新的future会适配一个函数,并且这个函数的参数就是第一个future的返回值。
map主要是将第一个future的结果映射到另外一个,然后返回一个future包装这个结果。
#[macro_use] extern crate futures;
extern crate tokio;
use futures::{Future, Async, Poll};
use std::fmt;
struct HelloWorld;
impl Future for HelloWorld {
// Item, Error是Future完成后的返回值
type Item = String;
type Error = ();
// 完成状态是 Async::Ready
// 阻塞状态是 Async::NotReady, 一般不主动返回这个状态
// 错误状态是 Error
fn poll(&mut self) -> Result, Self::Error> {
Ok(Async::Ready("Hello, world!".to_string()))
}
}
fn main(){
let f = HelloWorld{}.map(|v| println!("{}", v));
tokio::run(f);
}
The and_then combinator allows sequencing two asynchronous operations. Once the first operation completes, the value is passed to a function. The function uses that value to produce a new future and that future is then executed.
The difference between and_then and map is that and_then’s function returns a future where as map’s function returns a value.
and_then 按序执行两个异步操作。第一个future完成后,将值作为参数传递到and_then的参数函数中(作为函数的参数),生成一个新的future,然后执行第二个future。
and_then与map的区别是 and_then返回future, map返回的是一个值。即,and_then后的结果是一个future还需要继续向下调用才能拿到真正的值,而map是返回一个值,不需要继续run。
extern crate tokio;
extern crate bytes;
extern crate futures;
use tokio::io;
use tokio::net::TcpStream;
use futures::Future;
fn main1() {
let addr = "127.0.0.1:1234".parse().unwrap();
let f1 = TcpStream::connect(&addr)
.and_then(|socket| {
io::write_all(socket, b"hello world")
})
.map(|_| println!("write complete"))
.map_err(|_| println!("failed"));
tokio::run(f1);
}
fn main2() {
let addr = "127.0.0.1:1234".parse().unwrap();
let f1 = TcpStream::connect(&addr)
.and_then(|socket| {
io::write_all(socket, b"hello world")
})
.and_then(|(socket, _)| {
// read exactly 11 bytes
io::read_exact(socket, vec![0; 11])
})
.and_then(|(socket, buf)| {
println!("got {:?}", buf);
Ok(())
}).map_err(|e| println!("{:?}", e));
tokio::run(f1);
}
fn main(){
main1();
main2();
}
常用的组合函数:
map, map_err
)then, and_then, or_else
)select
)join
)Box::new
)catch_unwind
)TCP Demo:
extern crate tokio;
use tokio::prelude::*;
use tokio::io::copy;
use tokio::net::TcpListener;
fn main() {
// Bind the server's socket.
let addr = "127.0.0.1:12345".parse().unwrap();
let listener = TcpListener::bind(&addr)
.expect("unable to bind TCP listener");
// Pull out a stream of sockets for incoming connections
let server = listener.incoming()
.map_err(|e| eprintln!("accept failed = {:?}", e))
.for_each(|sock| {
// Split up the reading and writing parts of the
// socket.
let (reader, writer) = sock.split();
// A future that echos the data and returns how
// many bytes were copied...
let bytes_copied = copy(reader, writer);
// ... after which we'll print what happened.
let handle_conn = bytes_copied.map(|amt| {
println!("wrote {:} bytes", amt.0)
}).map_err(|err| {
eprintln!("IO error {:?}", err)
});
// Spawn the future as a concurrent task.
tokio::spawn(handle_conn)
});
// Start the Tokio runtime
tokio::run(server);
}
与Iterator一样,Future,Stream和Sink trait都配备了广泛的“适配器”方法。这些方法都使用接收对象并返回一个新的包装对象。
对于future,您可以使用适配器:
对于流,有一大组适配器,包括:
Sink trait目前具有较少的适配器
最后,可以使用拆分适配器将既是流又是接收器的对象分解为单独的流和接收对象。所有适配器都是零成本的,这意味着内部没有内存分配,并且实现将优化到您手动编写的内容。
extern crate futures;
extern crate tokio;
use std::env;
use std::io::stdin;
use std::net::SocketAddr;
use tokio::net::UdpSocket;
use tokio::prelude::*;
fn get_stdin_data() -> Result, Box> {
let mut buf = String::new();
stdin().read_line(&mut buf)?;
Ok(buf.into_bytes())
}
fn main() -> Result<(), Box> {
let remote_addr: SocketAddr = env::args()
.nth(1)
.unwrap_or("127.0.0.1:8080".into())
.parse()?;
// We use port 0 to let the operating system allocate an available port for us.
let local_addr: SocketAddr = if remote_addr.is_ipv4() {
"0.0.0.0:0"
} else {
"[::]:0"
}.parse()?;
let mut socket = UdpSocket::bind(&local_addr)?;
const MAX_DATAGRAM_SIZE: usize = 65_507;
let mut in_buf = Vec::new();
loop {
in_buf = get_stdin_data()?;
socket = socket.send_dgram(in_buf, &remote_addr)
.and_then(|(socket, _)| {
socket.recv_dgram(vec![0u8; MAX_DATAGRAM_SIZE])
})
.map(|(s, data, len, _)| {
println!(
"Received {} bytes:\n{}",
len,
String::from_utf8_lossy(&data[..len])
);
s
}).wait()?;
}
Ok(())
}
这里的recv_dgram/send_dgram
比较特殊,每次都move且构建一个socket, 所以,不能重复使用socket本身,需要在每次调用后重新为socket赋值(move)。
extern crate tokio;
extern crate futures;
use tokio::prelude::*;
use tokio::net::{TcpStream, TcpListener};
use std::net::SocketAddr;
use tokio::io;
fn main() {
let listen_addr = "127.0.0.1:8081".parse::().unwrap();
let server_addr = "127.0.0.1:9090".parse::().unwrap();
println!("Listen on: {}", listen_addr.to_string());
println!("proxy for: {}", server_addr.to_string());
let server = TcpListener::bind(&listen_addr).unwrap()
.incoming().map_err(|e| {
println!("incoming error: {:?}", e);
}).for_each(move |s| {
println!("incoming tcp: {}", s.peer_addr().unwrap().to_string());
let c_to_s = TcpStream::connect(&server_addr).and_then(|c| {
let (c_r, c_w) = c.split();
let (s_r, s_w) = s.split();
let con1 = io::copy(c_r, s_w).and_then(|(n, r, w)| {
io::shutdown(w).map(move |_| n)
});
let con2 = io::copy(s_r, c_w).and_then(|(n, r, w)| {
io::shutdown(w).map(move |_| n)
});
(con1, con2)
});
// map like a call function
let msg = c_to_s.map(|(c, s)| {
println!("close! {} {}", c, s);
}).map_err(|e| {
println!("error: {:?}", e);
});
tokio::spawn(msg);
Ok(())
});
tokio::run(server);
}
extern crate tokio;
extern crate futures;
use futures::future::lazy;
fn main() {
tokio::run(lazy(|| {
for i in 0..4 {
tokio::spawn(lazy(move || {
println!("Hello from task {}", i);
Ok(())
}));
}
Ok(())
}));
}
futures
库提供了一个sync
模块,这个模块包括了一些通道(channel)类型,它们是跨任务消息传递的理想选择。
oneshot
是用于发送单个值的通道。mpsc
是用于发送多个值(零或多个)的通道。extern crate tokio;
extern crate futures;
use futures::Future;
use futures::future::lazy;
use futures::sync::oneshot;
use std::{thread, time};
fn main() {
let task = lazy(|| {
let (tx, rx) = oneshot::channel();
tokio::spawn(lazy(|| {
let t = time::Duration::from_millis(3000);
thread::sleep(t);
tx.send("Hello,world");
Ok(())
}));
rx.and_then(|msg| {
println!("Got {}", msg);
Ok(())
}).map_err(|e| {
println!("error: {:?}", e);
}) // 注意这里没有分号,是返回值 Result
});
println!("run task, wait for msg!");
tokio::run(task);
}
多条信息发送
extern crate tokio;
extern crate futures;
use futures::{Future, stream, Stream, Sink};
use futures::future::lazy;
use futures::sync::mpsc;
use std::{thread, time};
fn main() {
let task = lazy(|| {
let (tx, rx) = mpsc::channel(1024);
tokio::spawn(
stream::iter_ok(0..10).fold(tx, |tx, i| {
let t = time::Duration::from_millis(1500);
thread::sleep(t);
tx.send(format!("msg from task {}", i)).map_err(|e| {
println!("send error: {:?}", e)
})
}).map(|_| ()) // 自身是返回值
);
rx.for_each(|msg| {
println!("Got {}", msg);
Ok(())
}).map_err(|e| {
println!("error: {:?}", e);
}) // 注意这里没有分号,是返回值
});
println!("run task, wait for msg!");
tokio::run(task);
}
tokio future是通过poll轮训得到执行的,并且必须通过两个future配合才能完成最终的调用
#[macro_use] extern crate futures;
extern crate tokio;
use futures::{Future, Async, Poll};
use std::fmt;
struct HelloWorld;
struct Display(T);
impl Future for Display
where
T: Future,
T::Item: fmt::Display,
{
type Item = ();
type Error = T::Error;
fn poll(&mut self) -> Poll<(), T::Error> {
let value = r#try_ready!(self.0.poll());
println!("{}", value);
Ok(Async::Ready(()))
}
}
impl Future for HelloWorld {
// Item, Error是Future完成后的返回值
type Item = String;
type Error = ();
// 完成状态是 Async::Ready
// 阻塞状态是 Async::NotReady, 一般不主动返回这个状态
// 错误状态是 Error
fn poll(&mut self) -> Result, Self::Error> {
Ok(Async::Ready("Hello, world!".to_string()))
}
}
fn main(){
let f = Display(HelloWorld);
tokio::run(f);
}
配合其他Future来完成
extern crate tokio;
#[macro_use] extern crate futures;
use tokio::net::{TcpStream, tcp::ConnectFuture};
use futures::{Future, Async, Poll};
struct GetPeerAddr {
connect: ConnectFuture,
}
impl Future for GetPeerAddr {
type Item = ();
type Error = ();
fn poll(&mut self) -> Poll {
match self.connect.poll() {
Ok(Async::Ready(socket)) => {
println!("peer address = {}", socket.peer_addr().unwrap());
Ok(Async::Ready(()))
}
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(e) => {
println!("failed to connect: {}", e);
Ok(Async::Ready(()))
}
}
}
}
fn main() {
let addr = "192.168.0.1:1234".parse().unwrap();
let connect_future = TcpStream::connect(&addr);
let get_peer_addr = GetPeerAddr {
connect: connect_future,
};
tokio::run(get_peer_addr);
}
Stream类似Future,但是可以持续产生不同的值,可以看作是future的异步产生器。
#[macro_use] extern crate futures;
use futures::{Future, Stream, Poll, Async};
use std::fmt;
pub struct Fibonacci {
curr: u64,
next: u64,
}
impl Fibonacci {
fn new() -> Fibonacci {
Fibonacci {
curr: 1,
next: 1,
}
}
}
impl Stream for Fibonacci {
type Item = u64;
// The stream will never yield an error
type Error = ();
fn poll(&mut self) -> Poll
stream进阶版
extern crate futures;
use futures::{stream, Stream};
fn fibonacci() -> impl Stream- {
stream::unfold((1, 1), |(curr, next)| {
let new_next = curr + next;
Some(Ok((curr, (next, new_next))))
})
}
fn main(){
tokio::run(
fibonacci().take(10)
.for_each(|num| {
println!("{}", num);
Ok(())
})
);
}
其中, unfold将一个初始值,展开为多个值(流)。fold是将多个值,合拢为一个值。
stream本身就是Result
map, map_err, and_then
).or_else
).take, take_while, skip, skip_while, filter, filter_map
).for_each, fold
).zip, chain, select
).extern crate tokio;
#[macro_use]
extern crate futures;
use tokio::io;
use tokio::net::{TcpListener, TcpStream};
use tokio::prelude::*;
use std::net::SocketAddr;
use futures::lazy;
use std::io::Error;
use std::{thread, time};
struct MyTcpSender {
stream: TcpStream,
count: i32,
}
impl Future for MyTcpSender {
type Item = ();
type Error = Error;
fn poll(&mut self) -> Poll {
let d = time::Duration::from_millis(500);
let mut i = 0;
while i < self.count {
let msg = format!("msg {}", i);
// try_ready! 很关键
try_ready!(self.stream.poll_write(msg.as_bytes()));
// self.stream.write_all(msg.as_bytes())?;
thread::sleep(d);
i += 1;
}
Ok(Async::Ready(()))
}
}
fn main() {
let addr = "0.0.0.0:8888";
let addr_s = addr.parse::().unwrap();
let listener = TcpListener::bind(&addr_s).map_err(|e| {
println!("bind error: {:?}", e)
}).unwrap();
let task = listener.incoming().for_each(|stream| {
println!("======");
// let m = io::write_all(stream, "21321").map(|_| println!("")).map_err(|e| println!(""));
let m = MyTcpSender { stream: stream, count: 100 }.map(|_| ()).map_err(|_| ());
tokio::spawn(m);
println!("-----");
Ok(())
}).map_err(|e| {
println!("incomming error: {:?}", e)
});
tokio::run(task);
}