Skip to content

Commit 6043c57

Browse files
committed
feat: 添加 DNS 预解析功能及超时配置
新增支持禁用 DNS 预解析和自定义 DNS 解析超时时间,扩展同步和异步 ping 相关接口及流式接口,完善示例代码。
1 parent e5a28da commit 6043c57

File tree

8 files changed

+312
-22
lines changed

8 files changed

+312
-22
lines changed

python/examples/basic_usage.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ def sync_examples():
4545
result = pinger.ping_once()
4646
print(f"IPv4-only ping: {result}")
4747

48+
# 禁用 DNS 预解析
49+
pinger = Pinger("8.8.8.8", dns_pre_resolve=False)
50+
result = pinger.ping_once()
51+
print(f"Ping without DNS pre-resolve: {result}")
52+
53+
# 自定义 DNS 解析超时
54+
pinger = Pinger("8.8.8.8", dns_resolve_timeout_ms=5000)
55+
result = pinger.ping_once()
56+
print(f"Ping with custom DNS timeout: {result}")
57+
4858

4959
async def async_examples():
5060
"""异步 API 示例"""

python/ping_rs/_ping_rs.pyi

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ class Pinger:
120120
interface: str | None = None,
121121
ipv4: bool = False,
122122
ipv6: bool = False,
123+
dns_pre_resolve: bool = True,
124+
dns_resolve_timeout_ms: int | None = None,
123125
) -> Pinger: ...
124126
def ping_once(self) -> PingResult:
125127
"""Execute a single ping synchronously."""
@@ -143,6 +145,8 @@ class AsyncPinger:
143145
interface: str | None = None,
144146
ipv4: bool = False,
145147
ipv6: bool = False,
148+
dns_pre_resolve: bool = True,
149+
dns_resolve_timeout_ms: int | None = None,
146150
) -> AsyncPinger: ...
147151
async def ping_once(self) -> PingResult:
148152
"""Execute a single ping asynchronously."""
@@ -167,6 +171,8 @@ class PingStream:
167171
ipv4: bool = False,
168172
ipv6: bool = False,
169173
max_count: int | None = None,
174+
dns_pre_resolve: bool = True,
175+
dns_resolve_timeout_ms: int | None = None,
170176
) -> PingStream: ...
171177
def try_recv(self) -> PingResult | None:
172178
"""Try to receive the next ping result without blocking."""
@@ -204,6 +210,8 @@ class AsyncPingStream:
204210
ipv4: bool = False,
205211
ipv6: bool = False,
206212
max_count: int | None = None,
213+
dns_pre_resolve: bool = True,
214+
dns_resolve_timeout_ms: int | None = None,
207215
) -> AsyncPingStream: ...
208216
def __aiter__(self) -> AsyncPingStream:
209217
"""Return self as an async iterator."""
@@ -223,6 +231,8 @@ def ping_once(
223231
interface: str | None = None,
224232
ipv4: bool = False,
225233
ipv6: bool = False,
234+
dns_pre_resolve: bool = True,
235+
dns_resolve_timeout_ms: int | None = None,
226236
) -> PingResult:
227237
"""Execute a single ping operation synchronously."""
228238
...
@@ -233,6 +243,8 @@ async def ping_once_async(
233243
interface: str | None = None,
234244
ipv4: bool = False,
235245
ipv6: bool = False,
246+
dns_pre_resolve: bool = True,
247+
dns_resolve_timeout_ms: int | None = None,
236248
) -> PingResult:
237249
"""Execute a single ping operation asynchronously."""
238250
...
@@ -245,6 +257,8 @@ def ping_multiple(
245257
interface: str | None = None,
246258
ipv4: bool = False,
247259
ipv6: bool = False,
260+
dns_pre_resolve: bool = True,
261+
dns_resolve_timeout_ms: int | None = None,
248262
) -> list[PingResult]:
249263
"""Execute multiple ping operations synchronously."""
250264
...
@@ -257,6 +271,8 @@ async def ping_multiple_async(
257271
interface: str | None = None,
258272
ipv4: bool = False,
259273
ipv6: bool = False,
274+
dns_pre_resolve: bool = True,
275+
dns_resolve_timeout_ms: int | None = None,
260276
) -> list[PingResult]:
261277
"""Execute multiple ping operations asynchronously."""
262278
...
@@ -268,6 +284,8 @@ def create_ping_stream(
268284
ipv4: bool = False,
269285
ipv6: bool = False,
270286
count: int | None = None,
287+
dns_pre_resolve: bool = True,
288+
dns_resolve_timeout_ms: int | None = None,
271289
) -> PingStream:
272290
"""Create a non-blocking ping stream."""
273291
...

src/lib.rs

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
//! This library provides both synchronous and asynchronous ping implementations,
66
//! as well as streaming interfaces for continuous ping operations.
77
8+
#![allow(clippy::too_many_arguments)] // 添加允许多参数的属性
9+
810
mod protocols;
911
mod types;
1012
mod utils;
@@ -24,17 +26,28 @@ pub use types::result::PingResult;
2426

2527
/// 创建非阻塞 ping 流
2628
#[pyfunction]
27-
#[pyo3(signature = (target, interval_ms=1000, interface=None, ipv4=false, ipv6=false, count=None))]
29+
#[pyo3(signature = (target, interval_ms=1000, interface=None, ipv4=false, ipv6=false, count=None, dns_pre_resolve=true, dns_resolve_timeout_ms=None))]
2830
fn create_ping_stream(
2931
target: &Bound<PyAny>,
3032
interval_ms: i64,
3133
interface: Option<String>,
3234
ipv4: bool,
3335
ipv6: bool,
3436
count: Option<usize>,
37+
dns_pre_resolve: bool,
38+
dns_resolve_timeout_ms: Option<i64>,
3539
) -> PyResult<PingStream> {
3640
// 直接使用 PingStream 的构造函数
37-
PingStream::new(target, interval_ms, interface, ipv4, ipv6, count)
41+
PingStream::new(
42+
target,
43+
interval_ms,
44+
interface,
45+
ipv4,
46+
ipv6,
47+
count,
48+
dns_pre_resolve,
49+
dns_resolve_timeout_ms,
50+
)
3851
}
3952

4053
/// 执行单次 ping(同步版本)
@@ -43,17 +56,27 @@ fn create_ping_stream(
4356
/// - `timeout_ms`: 等待响应的超时时间(毫秒),默认 1000ms
4457
/// 注意:内部实现中,这个值会被用作 `interval_ms` 传递给底层 ping 命令
4558
#[pyfunction]
46-
#[pyo3(signature = (target, timeout_ms=1000, interface=None, ipv4=false, ipv6=false))]
59+
#[pyo3(signature = (target, timeout_ms=1000, interface=None, ipv4=false, ipv6=false, dns_pre_resolve=true, dns_resolve_timeout_ms=None))]
4760
fn ping_once(
4861
target: &Bound<PyAny>,
4962
timeout_ms: i64,
5063
interface: Option<String>,
5164
ipv4: bool,
5265
ipv6: bool,
66+
dns_pre_resolve: bool,
67+
dns_resolve_timeout_ms: Option<i64>,
5368
) -> PyResult<PingResult> {
5469
// 创建 Pinger 实例
5570
// 注意:这里将 timeout_ms 作为 interval_ms 传递,因为 ping_once 中会将其用作超时时间
56-
let pinger = Pinger::new(target, timeout_ms, interface, ipv4, ipv6)?;
71+
let pinger = Pinger::new(
72+
target,
73+
timeout_ms,
74+
interface,
75+
ipv4,
76+
ipv6,
77+
dns_pre_resolve,
78+
dns_resolve_timeout_ms,
79+
)?;
5780

5881
// 执行 ping_once
5982
pinger.ping_once()
@@ -65,26 +88,36 @@ fn ping_once(
6588
/// - `timeout_ms`: 等待响应的超时时间(毫秒),默认 1000ms
6689
/// 注意:内部实现中,这个值会被用作 `interval_ms` 传递给底层 ping 命令
6790
#[pyfunction]
68-
#[pyo3(signature = (target, timeout_ms=1000, interface=None, ipv4=false, ipv6=false))]
91+
#[pyo3(signature = (target, timeout_ms=1000, interface=None, ipv4=false, ipv6=false, dns_pre_resolve=true, dns_resolve_timeout_ms=None))]
6992
fn ping_once_async<'py>(
7093
py: Python<'py>,
7194
target: &Bound<PyAny>,
7295
timeout_ms: i64,
7396
interface: Option<String>,
7497
ipv4: bool,
7598
ipv6: bool,
99+
dns_pre_resolve: bool,
100+
dns_resolve_timeout_ms: Option<i64>,
76101
) -> PyResult<Bound<'py, PyAny>> {
77102
// 创建 AsyncPinger 实例
78103
// 注意:这里将 timeout_ms 作为 interval_ms 传递,因为 ping_once 中会将其用作超时时间
79-
let pinger = AsyncPinger::new(target, timeout_ms, interface, ipv4, ipv6)?;
104+
let pinger = AsyncPinger::new(
105+
target,
106+
timeout_ms,
107+
interface,
108+
ipv4,
109+
ipv6,
110+
dns_pre_resolve,
111+
dns_resolve_timeout_ms,
112+
)?;
80113

81114
// 执行异步 ping_once
82115
pinger.ping_once(py)
83116
}
84117

85118
/// 执行多次 ping(同步版本)
86119
#[pyfunction]
87-
#[pyo3(signature = (target, count=4, interval_ms=1000, timeout_ms=None, interface=None, ipv4=false, ipv6=false))]
120+
#[pyo3(signature = (target, count=4, interval_ms=1000, timeout_ms=None, interface=None, ipv4=false, ipv6=false, dns_pre_resolve=true, dns_resolve_timeout_ms=None))]
88121
fn ping_multiple(
89122
target: &Bound<PyAny>,
90123
count: i32,
@@ -93,17 +126,27 @@ fn ping_multiple(
93126
interface: Option<String>,
94127
ipv4: bool,
95128
ipv6: bool,
129+
dns_pre_resolve: bool,
130+
dns_resolve_timeout_ms: Option<i64>,
96131
) -> PyResult<Vec<PingResult>> {
97132
// 创建 Pinger 实例
98-
let pinger = Pinger::new(target, interval_ms, interface, ipv4, ipv6)?;
133+
let pinger = Pinger::new(
134+
target,
135+
interval_ms,
136+
interface,
137+
ipv4,
138+
ipv6,
139+
dns_pre_resolve,
140+
dns_resolve_timeout_ms,
141+
)?;
99142

100143
// 执行 ping_multiple
101144
pinger.ping_multiple(count, timeout_ms)
102145
}
103146

104147
/// 执行多次 ping(异步版本)
105148
#[pyfunction]
106-
#[pyo3(signature = (target, count=4, interval_ms=1000, timeout_ms=None, interface=None, ipv4=false, ipv6=false))]
149+
#[pyo3(signature = (target, count=4, interval_ms=1000, timeout_ms=None, interface=None, ipv4=false, ipv6=false, dns_pre_resolve=true, dns_resolve_timeout_ms=None))]
107150
#[allow(clippy::too_many_arguments)] // 添加允许多参数的属性
108151
fn ping_multiple_async<'py>(
109152
py: Python<'py>,
@@ -114,9 +157,19 @@ fn ping_multiple_async<'py>(
114157
interface: Option<String>,
115158
ipv4: bool,
116159
ipv6: bool,
160+
dns_pre_resolve: bool,
161+
dns_resolve_timeout_ms: Option<i64>,
117162
) -> PyResult<Bound<'py, PyAny>> {
118163
// 创建 AsyncPinger 实例
119-
let pinger = AsyncPinger::new(target, interval_ms, interface, ipv4, ipv6)?;
164+
let pinger = AsyncPinger::new(
165+
target,
166+
interval_ms,
167+
interface,
168+
ipv4,
169+
ipv6,
170+
dns_pre_resolve,
171+
dns_resolve_timeout_ms,
172+
)?;
120173

121174
// 执行异步 ping_multiple
122175
pinger.ping_multiple(py, count, timeout_ms)

src/protocols/icmp/ping/async_ping.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub struct AsyncPinger {
1717
interface: Option<String>,
1818
ipv4: bool,
1919
ipv6: bool,
20+
dns_options: platform::DnsPreResolveOptions,
2021
}
2122

2223
#[pymethods]
@@ -29,30 +30,46 @@ impl AsyncPinger {
2930
/// - `interface`: Network interface to use (optional)
3031
/// - `ipv4`: Force IPv4 (default: false)
3132
/// - `ipv6`: Force IPv6 (default: false)
33+
/// - `dns_pre_resolve`: Enable DNS pre-resolution (default: true)
34+
/// - `dns_resolve_timeout_ms`: DNS resolution timeout in milliseconds (default: None, uses `interval_ms`)
3235
///
3336
/// # Errors
3437
/// - `PyValueError`: If `interval_ms` is negative, less than 100ms, or not a multiple of 100ms
3538
/// - `PyTypeError`: If the target cannot be converted to a string
3639
#[new]
37-
#[pyo3(signature = (target, interval_ms=1000, interface=None, ipv4=false, ipv6=false))]
40+
#[pyo3(signature = (target, interval_ms=1000, interface=None, ipv4=false, ipv6=false, dns_pre_resolve=true, dns_resolve_timeout_ms=None))]
3841
pub fn new(
3942
target: &Bound<PyAny>,
4043
interval_ms: i64,
4144
interface: Option<String>,
4245
ipv4: bool,
4346
ipv6: bool,
47+
dns_pre_resolve: bool,
48+
dns_resolve_timeout_ms: Option<i64>,
4449
) -> PyResult<Self> {
4550
let target_str = extract_target(target)?;
4651

4752
// 验证 interval_ms 参数
4853
let interval_ms_u64 = validate_interval_ms(interval_ms, "interval_ms")?;
4954

55+
// 处理 DNS 超时参数
56+
let dns_timeout = if let Some(timeout_ms) = dns_resolve_timeout_ms {
57+
let timeout_u64 = crate::utils::validation::i64_to_u64_positive(timeout_ms, "dns_resolve_timeout_ms")?;
58+
Some(std::time::Duration::from_millis(timeout_u64))
59+
} else {
60+
None
61+
};
62+
5063
Ok(Self {
5164
target: target_str,
5265
interval_ms: interval_ms_u64,
5366
interface,
5467
ipv4,
5568
ipv6,
69+
dns_options: platform::DnsPreResolveOptions {
70+
enable: dns_pre_resolve,
71+
timeout: dns_timeout,
72+
},
5673
})
5774
}
5875

@@ -66,14 +83,15 @@ impl AsyncPinger {
6683
let interface = self.interface.clone();
6784
let ipv4 = self.ipv4;
6885
let ipv6 = self.ipv6;
86+
let dns_options = self.dns_options;
6987

7088
future_into_py(py, async move {
7189
let options = create_ping_options(&target, interval_ms, interface, ipv4, ipv6);
7290

7391
let interval_duration = std::time::Duration::from_millis(interval_ms);
7492

7593
// 获取异步通道
76-
let mut receiver = platform::execute_ping_async(options)
94+
let mut receiver = platform::execute_ping_async(options, dns_options)
7795
.await
7896
.map_err(|e| PyErr::new::<PyRuntimeError, _>(format!("Failed to start ping: {e}")))?;
7997

@@ -120,14 +138,15 @@ impl AsyncPinger {
120138
let interface = self.interface.clone();
121139
let ipv4 = self.ipv4;
122140
let ipv6 = self.ipv6;
141+
let dns_options = self.dns_options;
123142

124143
future_into_py(py, async move {
125144
// 不传递 count 给底层 ping 命令,由 Rust 层控制接收数量
126145
let options = create_ping_options(&target, interval_ms, interface, ipv4, ipv6);
127146
let start_time = Instant::now();
128147

129148
// 获取异步通道
130-
let mut receiver = platform::execute_ping_async(options)
149+
let mut receiver = platform::execute_ping_async(options, dns_options)
131150
.await
132151
.map_err(|e| PyErr::new::<PyRuntimeError, _>(format!("Failed to start ping: {e}")))?;
133152

0 commit comments

Comments
 (0)