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客戶端去連接服務,併發送一條消息,服務端讀取後打印出來。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章