最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

利用Rust實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Ping應(yīng)用

 更新時(shí)間:2022年12月06日 16:47:26   作者:qingwave  
這兩年Rust火的一塌糊涂,甚至都燒到了前端,再不學(xué)習(xí)怕是要落伍了。最近翻了翻文檔,寫了個(gè)簡(jiǎn)單的Ping應(yīng)用練練手,感興趣的小伙伴可以了解一下

這兩年Rust火的一塌糊涂,甚至都燒到了前端,再不學(xué)習(xí)怕是要落伍了。最近翻了翻文檔,寫了個(gè)簡(jiǎn)單的Ping應(yīng)用練練手,被所有權(quán)折騰的夠嗆,相比起Golang上手難度大很多,現(xiàn)將開發(fā)中的一些問題總結(jié)如下,所有源碼見ring。

目標(biāo)

實(shí)現(xiàn)一個(gè)Ping,功能包含:

命令行解析

實(shí)現(xiàn)ICMP協(xié)議,pnet包中已經(jīng)包含了ICMP包定義,可以使用socket2庫(kù)發(fā)送

周期性發(fā)送Ping,通過多線程發(fā)送,再匯總結(jié)果

監(jiān)聽退出信號(hào)

命令行解析

系統(tǒng)庫(kù)std::env::args可以解析命令行參數(shù),但對(duì)于一些復(fù)雜的參數(shù)使用起來比較繁瑣,更推薦clap。利用clap的注解,通過結(jié)構(gòu)體定義命令行參數(shù)

/// ping but with rust, rust + ping -> ring
#[derive(Parser, Debug, Clone)] // Parser生成clap命令行解析方法
#[command(author, version, about, long_about = None)]
pub struct Args {
    /// Count of ping times
    #[arg(short, default_value_t = 4)] // short表示開啟短命名,默認(rèn)為第一個(gè)字母,可以指定;default_value_t設(shè)置默認(rèn)值
    count: u16,

    /// Ping packet size
    #[arg(short = 's', default_value_t = 64)]
    packet_size: usize,

    /// Ping ttl
    #[arg(short = 't', default_value_t = 64)]
    ttl: u32,

    /// Ping timeout seconds
    #[arg(short = 'w', default_value_t = 1)]
    timeout: u64,

    /// Ping interval duration milliseconds
    #[arg(short = 'i', default_value_t = 1000)]
    interval: u64,

    /// Ping destination, ip or domain
    #[arg(value_parser=Address::parse)] // 自定義解析
    destination: Address,
}

clap可以方便的指定參數(shù)命名、默認(rèn)值、解析方法等,運(yùn)行結(jié)果如下

?  ring git:(main) cargo run -- -h
   Compiling ring v0.1.0 (/home/i551329/work/ring)
    Finished dev [unoptimized + debuginfo] target(s) in 1.72s
     Running `target/debug/ring -h`
ping but with rust, rust + ping -> ring

Usage: ring [OPTIONS] <DESTINATION>

Arguments:
  <DESTINATION>  Ping destination, ip or domain

Options:
  -c <COUNT>            Count of ping times [default: 4]
  -s <PACKET_SIZE>      Ping packet size [default: 64]
  -t <TTL>              Ping ttl [default: 64]
  -w <TIMEOUT>          Ping timeout seconds [default: 1]
  -i <INTERVAL>         Ping interval duration milliseconds [default: 1000]
  -h, --help            Print help information
  -V, --version         Print version information

實(shí)現(xiàn)Ping

pnet中提供了ICMP包的定義,socket2可以將定義好的ICMP包發(fā)送給目標(biāo)IP,另一種實(shí)現(xiàn)是通過pnet_transport::transport_channel發(fā)送原始數(shù)據(jù)包,但需要過濾結(jié)果而且權(quán)限要求較高。

首先定義ICMP包

let mut buf = vec![0; self.config.packet_size];
let mut icmp = MutableEchoRequestPacket::new(&mut buf[..]).ok_or(RingError::InvalidBufferSize)?;
icmp.set_icmp_type(IcmpTypes::EchoRequest); // 設(shè)置為EchoRequest類型
icmp.set_icmp_code(IcmpCodes::NoCode);
icmp.set_sequence_number(self.config.sequence + seq_offset); // 序列號(hào)
icmp.set_identifier(self.config.id);
icmp.set_checksum(util::checksum(icmp.packet(), 1)); // 校驗(yàn)函數(shù)

通過socket2發(fā)送請(qǐng)求

let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::ICMPV4))?;
let src = SocketAddr::new(net::IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0);
socket.bind(&src.into())?; // 綁定源地址
socket.set_ttl(config.ttl)?;
socket.set_read_timeout(Some(Duration::from_secs(config.timeout)))?; // 超時(shí)配置
socket.set_write_timeout(Some(Duration::from_secs(config.timeout)))?;

???????// 發(fā)送
socket.send_to(icmp.packet_mut(), &self.dest.into())?;

最后處理相應(yīng),轉(zhuǎn)換成pnet中的EchoReplyPacket

let mut mem_buf = unsafe { &mut *(buf.as_mut_slice() as *mut [u8] as *mut [std::mem::MaybeUninit<u8>]) };
let (size, _) = self.socket.recv_from(&mut mem_buf)?;

???????// 轉(zhuǎn)換成EchoReply
let reply = EchoReplyPacket::new(&buf).ok_or(RingError::InvalidPacket)?;

至此,一次Ping請(qǐng)求完成。

周期性發(fā)送

Ping需要周期性的發(fā)送請(qǐng)求,比如秒秒請(qǐng)求一次,如果直接通過循環(huán)實(shí)現(xiàn),一次請(qǐng)求卡住將影響主流程,必須通過多線程來保證固定周期的發(fā)送。

發(fā)送請(qǐng)求

let send = Arc::new(AtomicU64::new(0)); // 統(tǒng)計(jì)發(fā)送次數(shù)
let _send = send.clone();
let this = Arc::new(self.clone());
let (sx, rx) = bounded(this.config.count as usize); // channel接受線程handler
thread::spawn(move || {
    for i in 0..this.config.count {
        let _this = this.clone();
        sx.send(thread::spawn(move || _this.ping(i))).unwrap(); // 線程中運(yùn)行ping,并將handler發(fā)送到channel中

        _send.fetch_add(1, Ordering::SeqCst); // 發(fā)送一次,send加1

        if i < this.config.count - 1 {
            thread::sleep(Duration::from_millis(this.config.interval));
        }
    }
    drop(sx); // 發(fā)送完成關(guān)閉channel
});
  • thread::spawn可以快速創(chuàng)建線程,但需要注意所有權(quán)的轉(zhuǎn)移,如果在線程內(nèi)部調(diào)用方法獲取變量,需要通過Arc原子引用計(jì)數(shù)
  • send變量用來統(tǒng)計(jì)發(fā)送數(shù),原子類型,并且用Arc包裹;this是當(dāng)前類的Arc克隆,會(huì)轉(zhuǎn)移到線程中
  • 第一個(gè)線程內(nèi)周期性調(diào)用ping(),并且其在單獨(dú)線程中運(yùn)行
  • 通過bounded來定義channel(類似Golang中的chan),用來處理結(jié)果,發(fā)送完成關(guān)閉

處理結(jié)果

let success = Arc::new(AtomicU64::new(0)); // 定義請(qǐng)求成功的請(qǐng)求
let _success = success.clone();
let (summary_s, summary_r) = bounded(1); // channel來判斷是否處理完成
thread::spawn(move || {
    for handle in rx.iter() {
        if let Some(res) = handle.join().ok() {
            if res.is_ok() {
                _success.fetch_add(1, Ordering::SeqCst); // 如果handler結(jié)果正常,success加1
            }
        }
    }
    summary_s.send(()).unwrap(); // 處理完成
});

第二個(gè)線程用來統(tǒng)計(jì)結(jié)果,channel通道取出ping線程的handler,如果返回正常則加1

處理信號(hào)

let stop = signal_notify()?; // 監(jiān)聽退出信號(hào)
select!(
    recv(stop) -> sig => {
        if let Some(s) = sig.ok() { // 收到退出信號(hào)
            println!("Receive signal {:?}", s);
        }
    },
    recv(summary_r) -> summary => { // 任務(wù)完成
        if let Some(e) = summary.err() {
            println!("Error on summary: {}", e);
        }
    },
);

通過select來處理信號(hào)(類似Golang中的select),到收到退出信號(hào)或者任務(wù)完成時(shí)繼續(xù)往下執(zhí)行。

信號(hào)處理

Golang中可以很方便的處理信號(hào),但在Rust中官方庫(kù)沒有提供類似功能,可以通過signal_hook與crossbeam_channel實(shí)現(xiàn)監(jiān)聽退出信號(hào)

fn signal_notify() -> std::io::Result<Receiver<i32>> {
    let (s, r) = bounded(1); // 定義channel,用來異步接受退出信號(hào)

    let mut signals = signal_hook::iterator::Signals::new(&[SIGINT, SIGTERM])?; // 創(chuàng)建信號(hào)

    thread::spawn(move || {
        for signal in signals.forever() { // 如果結(jié)果到信號(hào)發(fā)送到channel中
            s.send(signal).unwrap();
            break;
        }
    });

    Ok(r) // 返回接受channel
}

其他

很多吐槽人Golang的錯(cuò)誤處理,Rust也不遑多讓,不過提供了?語法糖,也可以配合anyhow與thiserror來簡(jiǎn)化錯(cuò)誤處理。

驗(yàn)證

Ping域名/IP

ring git:(main)  cargo run -- www.baidu.com 

PING www.baidu.com(103.235.46.40)
64 bytes from 103.235.46.40: icmp_seq=1 ttl=64 time=255.85ms
64 bytes from 103.235.46.40: icmp_seq=2 ttl=64 time=254.17ms
64 bytes from 103.235.46.40: icmp_seq=3 ttl=64 time=255.41ms
64 bytes from 103.235.46.40: icmp_seq=4 ttl=64 time=256.50ms

--- www.baidu.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3257.921ms

測(cè)試退出信息,運(yùn)行中通過Ctrl+C中止

cargo run 8.8.8.8 -c 10

PING 8.8.8.8(8.8.8.8)
64 bytes from 8.8.8.8: icmp_seq=1 ttl=64 time=4.32ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=64 time=3.02ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=64 time=3.24ms
^CReceive signal 2

???????--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2365.104ms

總結(jié)

Rust為了安全高效,通過引入所有權(quán)來解決GC問題,也帶來了許多不便,編程時(shí)必須要考慮到變量的聲明周期、借用等問題,所有語言都是在方便、性能、安全之間做權(quán)衡,要么程序員不方便,要么編譯器多做點(diǎn)功。換一個(gè)角度來說Bug總是不可避免的,在編譯階段出現(xiàn)總好過運(yùn)行階段。

到此這篇關(guān)于利用Rust實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Ping應(yīng)用的文章就介紹到這了,更多相關(guān)Rust實(shí)現(xiàn)Ping內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • rust的package,crate,module示例解析

    rust的package,crate,module示例解析

    rust提供了非常優(yōu)秀的包管理器cargo,我們可以使用crate,module,package來組織代碼,這篇文章主要介紹了rust的package,crate,module相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04
  • Rust?語言中符號(hào)?::?的使用場(chǎng)景解析

    Rust?語言中符號(hào)?::?的使用場(chǎng)景解析

    Rust?是一種強(qiáng)調(diào)安全性和速度的系統(tǒng)編程語言,這篇文章主要介紹了Rust?語言中符號(hào)?::?的使用場(chǎng)景,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2024-03-03
  • Rust  利用 chrono 庫(kù)實(shí)現(xiàn)日期和字符串互相轉(zhuǎn)換的示例

    Rust  利用 chrono 庫(kù)實(shí)現(xiàn)日期和字符串互相轉(zhuǎn)換的示例

    在Rust中,chrono庫(kù)提供了強(qiáng)大的日期和時(shí)間處理功能,使得日期與字符串之間的轉(zhuǎn)換變得簡(jiǎn)單,本文介紹了如何在Rust中使用chrono庫(kù)將日期轉(zhuǎn)換成字符串,以及如何將字符串解析為日期,對(duì)于需要進(jìn)行日期時(shí)間格式化、解析或進(jìn)行時(shí)區(qū)處理的開發(fā)者來說,chrono庫(kù)是一個(gè)不可或缺的工具
    2024-11-11
  • 一文弄懂rust聲明宏

    一文弄懂rust聲明宏

    Rust支持兩種宏,一種是聲明宏,一種是過程宏,本文主要介紹了一文弄懂rust聲明宏,通過聲明宏可以減少一些樣板代碼,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • Rust文件 launch.json作用大全

    Rust文件 launch.json作用大全

    launch.json 是 Visual Studio Code(VSCode)中的一個(gè)配置文件,主要用于配置調(diào)試器,本文給大家介紹Rust文件 launch.json 有什么用,感興趣的朋友跟隨小編一起看看吧
    2024-05-05
  • 前端基于Rust實(shí)現(xiàn)的Wasm進(jìn)行圖片壓縮的技術(shù)文檔(實(shí)現(xiàn)方案)

    前端基于Rust實(shí)現(xiàn)的Wasm進(jìn)行圖片壓縮的技術(shù)文檔(實(shí)現(xiàn)方案)

    在現(xiàn)代Web開發(fā)中,利用Rust編寫的圖片壓縮代碼可以編譯成WebAssembly(Wasm)模塊,Rust的內(nèi)存安全特性和Wasm的跨平臺(tái)能力,使得這種方案既高效又安全,對(duì)Rust?Wasm圖片壓縮實(shí)現(xiàn)方案感興趣的朋友一起看看吧
    2024-09-09
  • Rust常用功能實(shí)例代碼匯總

    Rust常用功能實(shí)例代碼匯總

    通過一系列實(shí)用示例展示了Rust在文件操作、網(wǎng)絡(luò)請(qǐng)求、并發(fā)編程、命令行工具以及數(shù)據(jù)庫(kù)操作等方面的應(yīng)用,這些示例不僅展示了 Rust 的強(qiáng)大功能,還提供了實(shí)際開發(fā)中的指導(dǎo)和參考,通過這些示例,您可以更好地理解 Rust 的特性,并將其應(yīng)用于您的項(xiàng)目中
    2024-12-12
  • 深入了解Rust中引用與借用的用法

    深入了解Rust中引用與借用的用法

    這篇文章主要為大家詳細(xì)介紹了Rust語言中引用與借用的使用,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的小伙伴可以了解一下
    2022-11-11
  • Rust調(diào)用函數(shù)操作符?.?和?::?的區(qū)別詳解

    Rust調(diào)用函數(shù)操作符?.?和?::?的區(qū)別詳解

    在Rust中,.和::操作符都可以用來調(diào)用方法,但它們的用法有所不同,所以本文就將詳細(xì)的給大家介紹一下.和::操作符的區(qū)別,感興趣的同學(xué)跟著小編一起來學(xué)習(xí)吧
    2023-07-07
  • Rust 中解析 JSON的方法

    Rust 中解析 JSON的方法

    要開始在 Rust 中使用 JSON,您需要安裝一個(gè)可以輕松操作 JSON 的庫(kù),目前可用的流行crate之一是 serde-json,在本文中,我們將討論如何在 Rust 中使用 JSON 解析庫(kù),以及比較最流行的庫(kù)及其性能
    2024-03-03

最新評(píng)論

朝阳区| 福泉市| 色达县| 手机| 资源县| 贺州市| 邛崃市| 班戈县| 互助| 吉安县| 通海县| 柘荣县| 印江| 丽水市| 株洲县| 西平县| 修武县| 集安市| 外汇| 南涧| 博乐市| 滨州市| 东莞市| 县级市| 宣汉县| 延寿县| 阿图什市| 天峻县| 含山县| 八宿县| 松阳县| 盈江县| 德保县| 黎城县| 莱西市| 剑川县| 滕州市| 嵩明县| 阿瓦提县| 宣汉县| 留坝县|