snapview / tokio-tungstenite

Future-based Tungstenite for Tokio. Lightweight stream-based WebSocket implementation
MIT License
1.88k stars 236 forks source link

Help wanted: WebSocket Client Blocking on Read, Preventing Concurrent Write #351

Closed skadefro closed 1 month ago

skadefro commented 1 month ago

I've been working on a WebSocket client to connect to a server and asynchronously handle reading/writing binary data. However, I've run into an issue where read() seems to block, which prevents messages from being sent until a message is first received from the server.

Code: Initially, I established a connection using:

let (socket, _response) = tokio_tungstenite::tungstenite::connect(strurl)
    .expect("Can't connect");

I used two separate threads started with tokio::spawn(async { to handle reading and writing. However, when calling read(), the client blocks, waiting for a message from the server. This behavior also affects sending messages: messages are not sent until the server sends a message first.

Attempted Fix: I suspected that the client might not be truly asynchronous, and calling read() might be causing blocking. So I tried splitting the socket:

let (socket, _response) = tokio_tungstenite::connect_async(strurl).await.expect("Can't connect");
let (write, read) = socket.split();

I hoped this would resolve the issue by separating reading and writing streams. However, the read() call still blocks. Additionally, calling read.peekable().count().await always return zero ?

Question:

I've been stuck on this for a few days, so any help or guidance would be greatly appreciated!

skadefro commented 1 month ago

I also tried switching to async-tungstenite instead of tokio-tungstenite, but I encountered the exact same issue: read() is still blocking write().

At this point, I feel like I must be missing something obvious, but I can't figure out what it is. Any further insight or suggestions would be greatly appreciated!

daniel-abramov commented 1 month ago

Judging from the description of the problem, I'm confident that it's an issue with the user code that might be caused by a misunderstanding of how Tokio tasks work or something similar (this is not rare for those who are new to async Rust, and we receive similar questions from time to time).

I don't have access to the original code to give any hints, but I'd strongly suggest checking our examples, which show how to do concurrent reading and writing with WebSockets: https://github.com/snapview/tokio-tungstenite/tree/master/examples. There, you will find an example of a server that achieves one of your goals.


  • Why does read() block, and why can't I send and receive messages concurrently?
  • Is there a proper way to peek for incoming messages before calling read()?
  • How can I achieve true asynchronous reading and writing over a WebSocket connection without read() blocking send()?
skadefro commented 1 month ago

Thank you, and yes I agree, i'm lacking some knowledge here. Running two instances of the client and one server from the example folder i can see it works without any issues. I will try and add more logic on top of those, to see if can figure out "where it breaks" compared to mine. Thank you for "nudging" me back to basics.