rust mio的例子

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客户端去连接服务,并发送一条消息,服务端读取后打印出来。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值