Rust libzmq客户端仅在send后调用recv才能成功发送消息问题
问题原因
ZMQ的所有网络IO都是由内部独立的IO线程异步处理的,你调用send方法时,只是把消息写入了ZMQ的内部发送队列,方法会立即返回,不会等待消息实际通过网络发送出去。
你的第一段代码在send执行完成后,main函数直接返回,进程随即退出,ZMQ上下文销毁时还没来得及把队列里的消息发送出去,消息就被丢弃了,所以服务端收不到。
第二段代码加了recv调用后,会阻塞等待服务端返回响应,这段阻塞等待的时间足够ZMQ的IO线程完成TCP握手、将消息实际发送到服务端,所以能正常工作。
解决方案
不需要调用recv也能正常发送的方案有以下两种常用实现:
方案1:调用flush方法主动等待消息发送完成
send之后直接调用flush方法,会阻塞直到所有队列里的消息都被实际发送出去,再往下执行。
修改后的代码如下:
use libzmq::{prelude::*, *, ClientBuilder, TcpAddr}; use std::time::Duration; fn main() -> Result<(), String> { let saddr = format!("{}:{}", "192.168.1.206","5540"); println!("Starting client for {}", saddr); let addr2: TcpAddr = saddr.try_into().unwrap(); let client = ClientBuilder::new() .connect(&addr2) .build().unwrap(); client.send("test").unwrap(); // 主动flush等待消息发送完成 client.flush().unwrap(); println!("Finished"); Ok(()) }
方案2:配置socket的linger参数
linger参数定义了socket关闭时,最多等待多长时间让未发送的消息发送完成。设置大于0的linger值后,进程退出销毁socket时,会等待指定时间让消息发完再销毁。
修改后的代码如下:
use libzmq::{prelude::*, *, ClientBuilder, TcpAddr}; use std::time::Duration; fn main() -> Result<(), String> { let saddr = format!("{}:{}", "192.168.1.206","5540"); println!("Starting client for {}", saddr); let addr2: TcpAddr = saddr.try_into().unwrap(); let client = ClientBuilder::new() .connect(&addr2) // 关闭时最多等待1秒让未发消息发送完成 .linger(Duration::from_secs(1)) .build().unwrap(); client.send("test").unwrap(); println!("Finished"); Ok(()) }
如果你的场景完全不需要接收服务端返回,也可以考虑换成PUSH/PULL的socket模式,更适配单向发送的需求。
内容的提问来源于stack exchange,提问作者mu5e
相关产品推荐
相关产品推荐

