You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何将子进程stdin存入变量后写入操作失效?

Dart语言服务器通信异常:两段逻辑一致的Rust代码为何表现不同?

我在尝试与Dart语言服务器进程通信时遇到了诡异问题——两段逻辑上完全一致的Rust实现,一个能让服务器收到消息并返回错误(说明输入已被接收),另一个却完全无法将消息传递给服务器,求解释背后的原因。

可行的实现代码

use std::{process::Stdio, time::Duration};

use tokio::{io::AsyncWriteExt, process::Command, time::sleep};

#[tokio::main]
async fn main() {
    let mut process = Command::new("dart")
        .args([
            "language-server",
            "--protocol=analyzer",
            "--protocol-traffic-log=logs.txt",
        ])
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .kill_on_drop(true)
        .spawn()
        .expect("failed to spawn process");

    process
        .stdin
        .take()
        .unwrap()
        .write_all(b"test")
        .await
        .unwrap();
    sleep(Duration::from_secs(5)).await;
}

对应的日志(log.txt)

注:输入格式无效,但关键是服务器明确收到了消息

1673866099379:Ver:1645600465579221612245:unknown.client.classic:unknown:1.33.1:2.18.0
1673866099410:Noti:{"event"::"server.connected","params"::{"version"::"1.33.1","pid"::16810}}
1673866099421:Req:test
1673866099422:Res:{"id"::"","error"::{"code"::"INVALID_REQUEST","message"::"Invalid request"}}

不可行的实现代码

唯一区别是将process.stdin.take().unwrap()的结果存入了变量:

use std::{process::Stdio, time::Duration};

use tokio::{io::AsyncWriteExt, process::Command, time::sleep};

#[tokio::main]
async fn main() {
    let mut process = Command::new("dart")
        .args([
            "language-server",
            "--protocol=analyzer",
            "--protocol-traffic-log=logs.txt",
        ])
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .kill_on_drop(true)
        .spawn()
        .expect("failed to spawn process");

    let mut stdin = process.stdin.take().unwrap();

    stdin.write_all(b"Test").await.unwrap();

    sleep(Duration::from_secs(5)).await;
}

对应的日志(log.txt)

1673866343914:Ver:1645600465579221612245:unknown.client.classic:unknown:1.33.1:2.18.0
1673866343942:Noti:{"event"::"server.connected","params"::{"version"::"1.33.1","pid"::16966}}

问题原因

核心在于Tokio异步Stdin句柄的缓冲区刷新时机:

  • 第一个实现中,write_all调用后,临时创建的Stdin句柄会立即被丢弃。当句柄被Drop时,Tokio会自动触发缓冲区刷新,确保数据被写入到Dart服务器的标准输入流中。
  • 第二个实现中,Stdin句柄被存入变量stdin,该变量会存活到函数末尾(sleep结束后)。在这段时间里,句柄不会被销毁,Tokio不会主动刷新缓冲区——异步IO默认采用缓冲机制,不会在每次write_all后立即写入底层流。因此Dart服务器根本没收到任何输入,自然不会生成请求日志。

解决方法

只需在write_all后手动调用flush()强制刷新缓冲区即可:

stdin.write_all(b"Test").await.unwrap();
stdin.flush().await.unwrap(); // 新增手动刷新逻辑

或者也可以通过让句柄提前被丢弃的方式触发自动刷新,比如将写入逻辑放在一个代码块中:

{
    let mut stdin = process.stdin.take().unwrap();
    stdin.write_all(b"Test").await.unwrap();
} // 代码块结束后stdin被Drop,自动刷新缓冲区
sleep(Duration::from_secs(5)).await;

内容的提问来源于stack exchange,提问作者Axot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 05:45:39