返回顶部
首页 > 资讯 > 精选 >怎么用Rust实现一个简单的Ping应用
  • 775
分享到

怎么用Rust实现一个简单的Ping应用

2023-07-04 18:07:29 775人浏览 安东尼
摘要

这篇文章主要介绍了怎么用Rust实现一个简单的Ping应用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么用Rust实现一个简单的Ping应用文章都会有所收获,下面我们一起来看看吧。目标实现一个Ping,功能

这篇文章主要介绍了怎么用Rust实现一个简单的Ping应用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么用Rust实现一个简单的Ping应用文章都会有所收获,下面我们一起来看看吧。

目标

实现一个Ping,功能包含:

命令行解析

实现ICMP协议,pnet包中已经包含了ICMP包定义,可以使用Socket2库发送

周期性发送Ping,通过多线程发送,再汇总结

监听退出信号

命令行解析

系统库std::env::args可以解析命令行参数,但对于一些复杂的参数使用起来比较繁琐,更推荐clap。利用clap的注解,通过结构体定义命令行参数

/// 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表示开启短命名,默认为第一个字母,可以指定;default_value_t设置默认值    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可以方便的指定参数命名、默认值、解析方法等,运行结果如下

➜  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

实现Ping

pnet中提供了ICMP包的定义,socket2可以将定义好的ICMP包发送给目标IP,另一种实现是通过pnet_transport::transport_channel发送原始数据包,但需要过滤结果而且权限要求较高。

首先定义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); // 设置为EchoRequest类型icmp.set_icmp_code(IcmpCodes::NoCode);icmp.set_sequence_number(self.config.sequence + seq_offset); // 序列号icmp.set_identifier(self.config.id);icmp.set_checksum(util::checksum(icmp.packet(), 1)); // 校验函数

通过socket2发送请求

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)))?; // 超时配置socket.set_write_timeout(Some(Duration::from_secs(config.timeout)))?;// 发送socket.send_to(icmp.packet_mut(), &self.dest.into())?;

最后处理相应,转换成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)?;// 转换成EchoReplylet reply = EchoReplyPacket::new(&buf).ok_or(RingError::InvalidPacket)?;

至此,一次Ping请求完成。

周期性发送

Ping需要周期性的发送请求,比如秒秒请求一次,如果直接通过循环实现,一次请求卡住将影响主流程,必须通过多线程来保证固定周期的发送。

发送请求

let send = Arc::new(AtomicU64::new(0)); // 统计发送次数let _send = send.clone();let this = Arc::new(self.clone());let (sx, rx) = bounded(this.config.count as usize); // channel接受线程handlerthread::spawn(move || {    for i in 0..this.config.count {        let _this = this.clone();        sx.send(thread::spawn(move || _this.ping(i))).unwrap(); // 线程中运行ping,并将handler发送到channel中        _send.fetch_add(1, Ordering::SeqCst); // 发送一次,send加1        if i < this.config.count - 1 {            thread::sleep(Duration::from_millis(this.config.interval));        }    }    drop(sx); // 发送完成关闭channel});
  • thread::spawn可以快速创建线程,但需要注意所有权的转移,如果在线程内部调用方法获取变量,需要通过Arc原子引用计数

  • send变量用来统计发送数,原子类型,并且用Arc包裹;this是当前类的Arc克隆,会转移到线程中

  • 第一个线程内周期性调用ping(),并且其在单独线程中运行

  • 通过bounded来定义channel(类似golang中的chan),用来处理结果,发送完成关闭

处理结果

let success = Arc::new(AtomicU64::new(0)); // 定义请求成功的请求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结果正常,success加1            }        }    }    summary_s.send(()).unwrap(); // 处理完成});

第二个线程用来统计结果,channel通道取出ping线程的handler,如果返回正常则加1

处理信号

let stop = signal_notify()?; // 监听退出信号select!(    recv(stop) -> sig => {        if let Some(s) = sig.ok() { // 收到退出信号            println!("Receive signal {:?}", s);        }    },    recv(summary_r) -> summary => { // 任务完成        if let Some(e) = summary.err() {            println!("Error on summary: {}", e);        }    },);

通过select来处理信号(类似Golang中的select),到收到退出信号或者任务完成时继续往下执行。

信号处理

Golang中可以很方便的处理信号,但在Rust中官方库没有提供类似功能,可以通过signal_hook与crossbeam_channel实现监听退出信号

fn signal_notify() -> std::io::Result<Receiver<i32>> {    let (s, r) = bounded(1); // 定义channel,用来异步接受退出信号    let mut signals = signal_hook::iterator::Signals::new(&[SIGINT, SIGTERM])?; // 创建信号    thread::spawn(move || {        for signal in signals.forever() { // 如果结果到信号发送到channel中            s.send(signal).unwrap();            break;        }    });    Ok(r) // 返回接受channel}

其他

很多吐槽人Golang的错误处理,Rust也不遑多让,不过提供了?语法糖,也可以配合anyhow与thiserror来简化错误处理。

验证

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

测试退出信息,运行中通过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

关于“怎么用Rust实现一个简单的Ping应用”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“怎么用Rust实现一个简单的Ping应用”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网精选频道。

--结束END--

本文标题: 怎么用Rust实现一个简单的Ping应用

本文链接: https://lsjlt.com/news/346710.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 怎么用Rust实现一个简单的Ping应用
    这篇文章主要介绍了怎么用Rust实现一个简单的Ping应用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么用Rust实现一个简单的Ping应用文章都会有所收获,下面我们一起来看看吧。目标实现一个Ping,功能...
    99+
    2023-07-04
  • 利用Rust实现一个简单的Ping应用
    目录目标命令行解析实现Ping周期性发送其他验证总结这两年Rust火的一塌糊涂,甚至都烧到了前端,再不学习怕是要落伍了。最近翻了翻文档,写了个简单的Ping应用练练手,被所有权折腾的...
    99+
    2022-12-08
    Rust实现Ping应用 Rust实现Ping Rust Ping
  • 使用python实现一个简单ping pong服务器
    目录依赖环境客户端/服务器架构介绍什么是客户端/服务器架构如何使用客户端/服务器架构缺点python socket编程写一个最简单的ping/pong服务总结依赖环境 本篇文章所依赖...
    99+
    2023-05-16
    python ping pong服务器 python ping pong
  • python实现一个简单的web应用框架
    目录引言写应用框架需要写底层服务器么uwsgi基本使用安装uwsgi配置uwsgiuwsgi常用配置uwsgi启服和停服启动一个demo写一个简单的web应用框架总结引言 本篇文章所...
    99+
    2023-05-18
    python web应用框架 python web
  • 如何使用rust实现简单的单链表
    目录前言1.链表节点的定义2.链表的定义3.实现从链表头部插入节点的prepend方法4.为链表实现Display trait定制链表的打印显示5.为链表实现翻转链表功能的rever...
    99+
    2024-04-02
  • 用Python实现一个简单的WebSoc
    ubuntu下python2.76 windows python 2.79, chrome37 firefox35通过 代码是在别人(cddn有人提问)基础上改的, 主要改动了parsedata和sendmessage这2个函数. 改代码...
    99+
    2023-01-31
    简单 Python WebSoc
  • 怎么用rust实现单链表
    这篇文章将为大家详细讲解有关怎么用rust实现单链表,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。前言今天的目标是用rust实现一个简单的单链表LinkedList,同时为此链表提供从头部插入元素(头插法...
    99+
    2023-06-29
  • 利用Rust编写一个简单的字符串时钟
    目录1、简介2、用到的知识点2.1 取utc时间2.2 图片变换为像素图案2.3 字符方式显示当前时间2.4 时间刷新1、简介 用rust写的一个简单的练手的demo,一个字符串时钟...
    99+
    2022-12-26
    Rust字符串时钟 Rust 时钟
  • 怎么用java实现一个简单的序列化
    小编给大家分享一下怎么用java实现一个简单的序列化,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!  package test;import Java....
    99+
    2023-06-03
  • Android怎么实现一个简单的单词本
    这篇文章主要介绍了Android怎么实现一个简单的单词本的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Android怎么实现一个简单的单词本文章都会有所收获,下面我们一起来看看吧。本文基于Java实现了一个简单...
    99+
    2023-06-29
  • 怎么使用JavaScript实现一个简单的通讯录
    这篇文章主要介绍“怎么使用JavaScript实现一个简单的通讯录”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“怎么使用JavaScript实现一个简单的通讯录”文章能帮助大家解决问题。创建通讯录对...
    99+
    2023-07-06
  • Vue怎么实现简单的todo应用
    今天小编给大家分享一下Vue怎么实现简单的todo应用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1. 引用vue.js&...
    99+
    2023-07-04
  • 怎么创建一个简单的SpringBoot应用程序
    要创建一个简单的Spring Boot 应用程序,可以按照以下步骤操作: 使用 Spring Initializr 创建一个新的...
    99+
    2024-04-09
    SpringBoot
  • 用Python实现一个简单的用户系统
    目录前言正文总结前言  如标题所说,这是一个非常简单的程序,并不涉及任何高深的学问,更适合一些刚入手Python的新人研究一下基础内容的用法,此案列对于有些编程经验的人来讲...
    99+
    2024-04-02
  • 用Python实现一个简单的线程池
    线程池的概念是什么?在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是 如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能...
    99+
    2023-01-31
    线程 简单 Python
  • pyscript的简单应用实现
    目录1. 什么是pyscript2.pyscript的应用2-1.下载2-2. CDN2-3. 你的第一个pyscript应用最近在 PyCon US 2022 上,Anaconda...
    99+
    2024-04-02
  • 一个应用启动另一个应用,实现应用间通讯,用bundle就行了,Android aidl简单demo 实现ipc通讯。
    一个应用启动另一个应用 重点:保证包名,和启动activity正确; 实现应用间通讯,用bundle就行了 @Override protected void ...
    99+
    2022-06-06
    启动 aidl ipc demo bundle Android
  • 怎么用PHP实现简单的聊天室应用
    本篇内容介绍了“怎么用PHP实现简单的聊天室应用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!介绍聊天应用程序在网上非常常见。开发人员在构建...
    99+
    2023-06-17
  • TypeScript创建一个简单Web应用
    目录安装TypeScript构建你的第一个TypeScript文件编译代码类型注解接口类运行TypeScript Web应用实践项目地址安装TypeScript 获取TypeScri...
    99+
    2024-04-02
  • CSS怎么实现一个简单的Loading效果
    在前端开发中,Loading是一个非常常见的功能。当网页数据加载时,我们希望能够在用户等待的同时,给其一个视觉上的反馈,来增强用户体验。在这篇文章中,我们将通过CSS来实现一个简单的Loading效果。1.使用Animation动画实现Lo...
    99+
    2023-05-14
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作