Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simpler client example #137

Open
kwinz opened this issue Nov 23, 2020 · 16 comments
Open

Simpler client example #137

kwinz opened this issue Nov 23, 2020 · 16 comments

Comments

@kwinz
Copy link

kwinz commented Nov 23, 2020

Hi, the current examples/client.rs does redirect stdin and stdout to the WebSocket.
Splitting the Socket into send and receive and pining it in place and then handing one pair off via channel to a new Tokio Task that separately processes copying input to output.

I have been programming Rust less than two days and I wanted to do a simple WebSocket project. I find this example completely unapproachable. Moreover I think even unmodified this example buffers too much and it does not immediately send data on newline which makes it hard for me to understand if what I modified off the example is working or if it's a problem with buffering.

Can you please add a simpler Text based (non binary) example where you:

  1. Send a simple string ending in a newline.
  2. Flush this line immediately over the network.
  3. And then in a loop receive the response line by line and print it immediately until the program terminates.

Once I understand that I can do something more involved.

@kwinz
Copy link
Author

kwinz commented Nov 23, 2020

This is what I have so far:

use tokio::io::{AsyncWriteExt, Result};
use tokio_tungstenite::{connect_async, tungstenite::protocol::Message};
use futures_util::{StreamExt, SinkExt};

#[tokio::main]
pub async fn main() -> Result<()> {
    println!("Hello, tokio-tungstenite!");

    let url = url::Url::parse("wss://ws.kraken.com").unwrap();

    let (ws_stream, _response) = connect_async(url).await.expect("Failed to connect");
    println!("WebSocket handshake has been successfully completed");

    let (mut write, read) = ws_stream.split();

    println!("sending");

    write.send(Message::Text(r#"{
        "event": "ping",
        "reqid": 42
      }"#.to_string()+"\n")).await.unwrap();

    println!("sent");

    let read_future = read.for_each(|message| async {
        println!("receiving...");
         let data = message.unwrap().into_data();
         tokio::io::stdout().write(&data).await.unwrap();
         println!("received...");
    });

    read_future.await;

    Ok(())
}

I could have posted this in chat, but I really think that this repository would benefit from a simpler example, that's why I created an issue.

@daniel-abramov
Copy link
Member

Hm, the only noticeable difference that I can spot is that the code that you propose to add to the examples, would send one single message and then only read the messages from the websocket and print them to the stdout. Was that the intention?

@kwinz
Copy link
Author

kwinz commented Nov 23, 2020

The intention is to have a simplified example with "less moving parts", no extra functions, no closure, no extra Task, no channel. Just a minimal example for beginners like me that are just starting to learn Rust, want to experiment with Tokio and that see tokio-tungstenite for the first time. I think the other difference besides the one that you mentioned is using Strings and one off read.next() and write.send(Message...) invocations instead of Stream and Sink magic.
In the mean time I discovered the ordinary tungstenite-rs crate covers exactly that beginner example that I had in mind: https://github.com/snapview/tungstenite-rs/blob/master/examples/client.rs
One single message and then only read the messages from the websocket and print them to stdout.
I am not asking you to replace the example. The existing example shows how Futures allow elegant composition and concurrency. Just amend another one.

@kwinz
Copy link
Author

kwinz commented Nov 23, 2020

PS: Where would be an appropriate place to ask beginner questions about this crate? I can't quite figure out how to pass the returns of ws_stream.split() to fns as parameters.
Here https://discord.gg/tokio ?
Thanks in advance!

Tutorials that I read so far:

@daniel-abramov
Copy link
Member

Sorry for the late reply, yeah, you can post it to tokio, I mean such things are not strictly speaking about tokio-tungstenite, they are rather Tokio related, so I think if you post it on Tokio's discord (or StackOverflow), there would be someone who can help.

@arthmis
Copy link

arthmis commented Mar 26, 2021

@kwinz I like your example and it actually helped me figure out what I was missing, regarding sending messages, when using this library. Do you think you can have your example serialize to JSON? I know it would add a bit of complexity but I feel like new users will probably want to do something like that. It seems like your example is doing it manually. I'm not sure if it is meant to be a raw string that represents JSON.

@jnordwick
Copy link

The examples are pretty poor. Send examples aren't very good. A couple are writing to stdout, which isn't' useful at all as a teaching tool. And none of them are good at concentrating on a small pieces and showing it clearly.

@black-puppydog
Copy link

Just to add my two cents: I agree that simpler examples would help. Being relatively new to tokio, async, rust in general, it was pretty difficult for me to disentangle the whole stdin/stdout, channels, and websocket logic in the client.rs example.

For someone who understands all of these in detail and has experience using them, the above example might seem overly simplistic, but to a newcomer it nicely isolates the websockets from everything else.

Thanks @kwinz for making a simpler example!

@Fanisus
Copy link

Fanisus commented Jun 25, 2023

@kwinz Your code example really helped me a lot. Thanks

@formbook
Copy link

3 years later and a significant (for github) amount of thumbs up, and they still haven't addressed this, when it's very low-hanging fruit to help a lot of beginners.

Utterly bizarre.

use tokio_tungstenite::tungstenite::protocol::Message;
use tokio_tungstenite::connect_async;
use futures::{ SinkExt, StreamExt };

#[tokio::main]
async fn main() {
    let url = "ws://localhost:3000/socket";

    // connect to socket
    if let Ok((mut socket, _)) = connect_async(url).await {
        println!("Successfully connected to the WebSocket");
        
        // create message
        let message = Message::from("message");

        // send message
        if let Err(e) = socket.send(message).await {
            eprintln!("Error sending message: {:?}", e);
        }

        // recieve response
        if let Some(Ok(response)) = socket.next().await {
            println!("{response}");
        }
    } else {
        eprintln!("Failed to connect to the WebSocket");
    }
}

@sdroege
Copy link
Contributor

sdroege commented Nov 11, 2023

@tesioai Why don't you or someone else make a PR with such an example then? You'd need to document how to run it and what the other side of the socket has to be though.

@formbook
Copy link

I dont see why the other side of the socket is relevant in a client example, users looking to connect to a socket will generally have a url for that socket. The current client example seems to take a url as an argument.

@ekkaiasmith
Copy link

It would be good to update the example because read_stdin is deprecated

@aurkaxi
Copy link

aurkaxi commented Dec 27, 2023

thank you tesioai

@datadius
Copy link

datadius commented Feb 3, 2024

thanks a lot for this, @tesioai

@hiroyuki-fujii-td
Copy link

thank you !! @tesioai

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests