mio是rust的一个低级非阻塞api的网络库
文档地址:https://docs.rs/mio/0.6.20/mio/
官方的介绍:A fast, low-level IO library for Rust focusing on non-blocking APIs, event notification, and other useful utilities for building high performance IO apps.
一个快速、低级别的Rust异步事件驱动的非阻塞API的IO网络库,主要用于构建高性能IO应用程序。
底层依赖操作系统的epoll、 kqueue、 IOCP机制,linux系统使用epoll机制,kqueue、 IOCP分别是mac和windows下类似epoll机制,具体可以百度。
epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
java的nio在linux系统使用epoll机制,mio就类似java的netty框架。
下面是mio官方的例子(重构了下):
库依赖:
[dependencies]
mio = "0.6"
use mio::net::{TcpListener,TcpStream };
use std::net::SocketAddr;
use mio::{Events, Poll, PollOpt, Ready, Token};
use std::collections::HashMap;
use std::io::{self, Read};
use std::thread;
// 允许服务连接的最大数
const MAX_SOCKETS: usize = 32;
// Pick a token that will not be used by any other socket and use that one
// for the listener. 选一个不能被其它socket连接使用的值做为listener监听绑定的token值
const LISTENER: Token = Token(1024);//这里1024做为值是,因为最大连接值为32,从0开始分配不会使用到1024
fn main() -> io::Result<()> {
// The `Poll` instance
let poll = Poll::new()?;
// Tcp listener
let listener = TcpListener::bind(&"127.0.0.1:3000".parse().unwrap())?;
// Register the listener
poll.register(&listener, LISTENER, Ready::readable(), PollOpt::edge())?;
let addr = listener.local_addr()?;
connect_server(addr);
// Event storage
let mut events = Events::with_capacity(1024);
// Read buffer, this will never actually get filled
let mut buf = [0; 256];
// Used to store the sockets.
let mut sockets = HashMap::new();
let mut next_socket_index = 0;//token index
// The main event loop
loop {
// Wait for events
poll.poll(&mut events, None)?;
for event in &events {
match event.token() {//这里使用了就绪事件token,从map里获取到可用的连接,
//在这个例子里,只注册了监听连接事件和连接到的socket的可读事件。
LISTENER => {//发现是监听就绪的token
listen(&listener,&poll,&mut sockets,&mut next_socket_index)?;
if next_socket_index == MAX_SOCKETS {
return Ok(());
}
}
token => {//就绪读事件
// 一直循环事件
loop { //根据获取到通知事件的token,从sockets map中取出socket
match sockets.get_mut(&token).unwrap().read(&mut buf) {
Ok(0) => {//读取字节长度为0,判断为关闭,移出此socket
// Socket is closed, remove it from the map
sockets.remove(&token);
break;
}
Ok(len) => {//读取成功,把字节数组转为字符串打印出来
println!("{}", String::from_utf8(Vec::from(&buf[0..len])).unwrap());
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
// Socket is not ready anymore, stop reading
// socket未就绪,停止读,跳出循环,否则一直读
break;
}
e => panic!("err={:?}", e), // Unexpected error
}
}
}
}
}
}
}
fn connect_server(addr :SocketAddr){
for i in 0..(MAX_SOCKETS + 1) {
use std::io::Write;
use std::net::TcpStream;
thread::spawn( move || {
let mut conn = TcpStream::connect(&addr).unwrap();
// conn.set_nonblocking(true); 可以使用非阻塞方式写入,参考标准库
conn.write_all(format!("{} hello rust mio\n", i).as_bytes())
.unwrap();
});
}
}
fn listen(
listener: &TcpListener,
poll: &Poll,
sockets: &mut HashMap<Token, TcpStream>, next_socket_index :&mut usize
) -> io::Result<()> {
loop {
match listener.accept() {
Ok((socket, _)) => {
// 创建一个token 下面绑定事件使用
let token = Token(*next_socket_index);
*next_socket_index += 1;// 从0开始分配不会使用到1024,1024的值被listener绑定事件时使用了
// Register the new socket w/ poll
// 把这个新socket的读事件注册到poll中
poll.register(&socket, token, Ready::readable(), PollOpt::edge())?;
//保存socket连接到hashmap中,供后续使用
sockets.insert(token, socket);
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
// Socket is not ready anymore, stop accepting
//还没有socket连接请求,跳出循环,执行其它事件处理
break;
}
e => panic!("err={:?}", e), // Unexpected error
}
}
Ok(())
}
上面的例子主要是服务端监听3000端口,模拟创建33客户端去连接服务,并发送一条消息,服务端读取后打印出来。