Tokio Tungstenite WebSocket Split

Split a WebSocket stream into independent sink and stream halves, running concurrent send and receive loops with heartbeats.

RustData Fetching

Code

snippet
use futures_util::{SinkExt, StreamExt};
use tokio_tungstenite::{connect_async, tungstenite::Message};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let (ws_stream, _response) = connect_async("wss://stream.example.com/feed").await?;

    // 拆分为独立的读半与写半:两个方向并发推进互不阻塞
    let (mut write, mut read) = ws_stream.split();

    let mut heartbeat = tokio::time::interval(std::time::Duration::from_secs(25));
    let mut received = 0usize;

    loop {
        tokio::select! {
            // 读方向:服务器推送的消息
            msg = read.next() => {
                match msg {
                    Some(Ok(Message::Text(text))) => {
                        received += 1;
                        println!("recv: {} bytes", text.len());
                    }
                    Some(Ok(Message::Ping(ping))) => {
                        // 协议层心跳:自动回 pong
                        write.send(Message::Pong(ping)).await?;
                    }
                    Some(Ok(_)) => {}
                    Some(Err(err)) => return Err(err.into()),
                    None => break, // 服务器关闭连接
                }
            }

            // 写方向:应用层心跳与订阅指令
            _ = heartbeat.tick() => {
                write.send(Message::Text(r#"{"op":"ping"}"#.into())).await?;
            }
        }

        if received >= 5 {
            break;
        }
    }

    write.send(Message::Close(None)).await?;
    println!("received {received} messages");
    Ok(())
}