I've written an echo server and client in Rust. Here is my code:
Server:
use std::net::{TcpListener, TcpStream};
use std::thread;
use std::io::Write;
use std::io::BufReader;
use std::io::BufRead;
use std::io::BufWriter;
fn handle_connection(stream: TcpStream) {
let stream_clone = stream.try_clone().unwrap();
let mut reader = BufReader::new(stream);
let mut writer = BufWriter::new(stream_clone);
loop {
let mut s = String::new();
reader.read_line(&mut s).unwrap();
writer.write(s.as_bytes()).unwrap();
}
}
fn main() {
let listener = TcpListener::bind("127.0.0.1:8888")
.unwrap();
for stream in listener.incoming() {
thread::spawn(move || {
handle_connection(stream.unwrap());
});
}
}
Client:
use std::net::TcpStream;
use std::io;
use std::io::Write;
use std::io::BufReader;
use std::io::BufRead;
use std::io::BufWriter;
fn main() {
let stream = TcpStream::connect("127.0.0.1:8888")
.unwrap();
let stream_clone = stream.try_clone().unwrap();
let mut reader = BufReader::new(stream);
let mut writer = BufWriter::new(stream_clone);
loop {
let mut s = String::new();
let mut response = String::new();
io::stdin().read_line(&mut s).unwrap();
writer.write(s.as_bytes()).unwrap();
reader.read_line(&mut response).unwrap();
println!("{}", response.trim());
}
}
When I test the code, the server don't respond at all. My guess is that something is wrong with the write method. Am I right, or is there another reason?
You need to flush the buffers: writer.flush()
Fixed server:
use std::net::{TcpListener, TcpStream};
use std::thread;
use std::io::Write;
use std::io::BufReader;
use std::io::BufRead;
use std::io::BufWriter;
fn handle_connection(stream: TcpStream) {
let stream_clone = stream.try_clone().unwrap();
let mut reader = BufReader::new(stream);
let mut writer = BufWriter::new(stream_clone);
loop {
let mut s = String::new();
reader.read_line(&mut s).unwrap();
writer.write(s.as_bytes()).unwrap();
writer.flush().unwrap();
}
}
fn main() {
let listener = TcpListener::bind("127.0.0.1:8888")
.unwrap();
for stream in listener.incoming() {
thread::spawn(move || {
handle_connection(stream.unwrap());
});
}
}
Client:
use std::net::TcpStream;
use std::io;
use std::io::Write;
use std::io::BufReader;
use std::io::BufRead;
use std::io::BufWriter;
fn main() {
let stream = TcpStream::connect("127.0.0.1:8888")
.unwrap();
let stream_clone = stream.try_clone().unwrap();
let mut reader = BufReader::new(stream);
let mut writer = BufWriter::new(stream_clone);
loop {
let mut s = String::new();
let mut response = String::new();
io::stdin().read_line(&mut s).unwrap();
writer.write(s.as_bytes()).unwrap();
writer.flush().unwrap();
reader.read_line(&mut response).unwrap();
println!("{}", response.trim());
}
}
Related
The following code is meant to print There is page two. if it finds a certain div on this website:
use reqwest;
use select::document::Document;
use select::predicate::Name;
use std::io;
static mut DECIDE: bool = false;
fn page_two_filter(x: &str, url: &str) {
if x == "pSiguiente('?pagina=2')" {
unsafe {
DECIDE = true;
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Give me the URL with the search results?");
let mut url = String::new();
io::stdin()
.read_line(&mut url)
.expect("Failed to read line");
let url = url.trim();
let html = reqwest::get(url).await?.text().await?;
Document::from(html.as_str())
.find(Name("div"))
.filter_map(|n| n.attr("onclick"))
.for_each(|x| page_two_filter(x, url));
unsafe {
if DECIDE == true {
println!("There is page two.")
}
}
Ok(())
}
Dependencies from Cargo.toml
[dependencies]
futures = "0.3.15"
reqwest = "0.11.9"
scraper = "0.12.0"
select = "0.5.0"
tokio = { version = "1", features = ["full"] }
Is there a safer way, i.e. without the unsafe blocks of code, of doing what that code does?
Wanting to avoid global mutable variables, I've tried with redefining page_two_filter and an if statement with the result of the call to page_two_filter, like so:
fn page_two_filter(x: &str, url: &str) -> bool {
if x == "pSiguiente('?pagina=2')" {
return true;
}
false
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Give me the URL with the search results?");
let mut url = String::new();
io::stdin()
.read_line(&mut url)
.expect("Failed to read line");
let url = url.trim();
let html = reqwest::get(url).await?.text().await?;
if Document::from(html.as_str())
.find(Name("div"))
.filter_map(|n| n.attr("onclick"))
.for_each(|x| page_two_filter(x, url))
{
println!("There is page two.")
}
Ok(())
}
but compiler does not allow me doing this saying:
mismatched types expected `()`, found `bool`
Instead of for_each(), I guess you need find().
This returns Some( found_element ) if found or None if not found.
You can then use the Option returned by find() with if let, match, is_some()...
if let Some(_) = Document::from(html.as_str())
.find(Name("div"))
.filter_map(|n| n.attr("onclick"))
.find(|x| page_two_filter(x, url))
{
println!("There is page two.")
}
First of all, the
mismatched types expected (), found bool
error is because there is no semicolon after the println statement in the for_each closure.
Secondly, the filter is actually a one-liner, which could be integrated in that very closure
fn page_two_filter(x: &str, url: &str) -> bool {
x == "pSiguiente('?pagina=2')"
}
Lastly, you already use various iterator methods, so why not continue?
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Give me the URL with the search results?");
let mut url = String::new();
io::stdin().read_line(&mut url).expect("Failed to read line");
let url = url.trim();
let html = reqwest::get(url).await?.text().await?;
if let Some(_) = Document::from(html.as_str())
.find(Name("div"))
.filter_map(|n| n.attr("onclick"))
.find_map(|attr| if attr == "pSiguiente('?pagina=2')" {
Some(true)
} else {
None
}) {
println!("There is page two.");
}
Ok(())
}
You can use Iterator::any which returns true on first find of condition, false otherwise:
fn page_two_filter(x: &str, url: &str) -> bool {
x == "pSiguiente('?pagina=2')"
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Give me the URL with the search results?");
let mut url = String::new();
io::stdin()
.read_line(&mut url)
.expect("Failed to read line");
let url = url.trim();
let html = reqwest::get(url).await?.text().await?;
let found = Document::from(html.as_str())
.find(Name("div"))
.filter_map(|n| n.attr("onclick"))
.any(|x| page_two_filter(x, url));
if found {
println!("There is page two.");
}
}
I have this piece of code which is supposed to serialize a futures::stream::Stream to a Write. I want this code to return Err if write_all fails, but I don't see a way to get that out of the callback to for_each. I found How to send data through a futures Stream by writing through the io::Write trait?, but I don't understand how to make it work in my situation.
use std::io::Write;
use std::error::Error;
use futures::stream::StreamExt;
pub async fn download(url: &str, mut dest: impl Write) -> Result<(), Box<dyn Error>> {
let byte_stream = reqwest::get(url).await?.bytes_stream();
byte_stream.for_each(|bytes| {
if let Ok(bytes) = bytes {
dest.write_all(&bytes).expect("failed to write");
}
futures::future::ready(())
}).await;
Ok(())
}
Cargo.toml
[dependencies]
reqwest = { version = "0.11", features = ["json", "stream"] }
tokio = { version = "1", features = ["full"] }
futures = "0.3"
bytes = "1"
I'd probably use StreamExt::next and write the loop out a bit more manually:
use futures::stream::StreamExt;
use std::{error::Error, io::Write};
pub async fn download(url: &str, mut dest: impl Write) -> Result<(), Box<dyn Error>> {
let mut byte_stream = reqwest::get(url).await?.bytes_stream();
while let Some(bytes) = byte_stream.next().await {
let bytes = bytes?;
dest.write_all(&bytes)?;
}
Ok(())
}
Notes:
These is TryStreamExt::try_for_each which can be used here, but it wants to take ownership of dest so I went in a different direction.
Mixing synchronous IO (std::io::Write) and asynchronous IO (reqwest) is a bad idea without taking special care, which is not demonstrated in this answer.
See also:
What is the best approach to encapsulate blocking I/O in future-rs?
I'm trying to kick off some async tasks in rust, then await them later in the code. Here's a simplified version of my code:
async fn my_async_fn() -> i64 {
return 0;
}
async fn main() {
let mut futures = HashMap::new();
futures.insert("a", my_async_fn());
// this is where I would do other work not blocked by these futures
let res_a = futures.get("a").expect("unreachable").await;
println!("my result: {}", res_a);
}
But when I try to run this, I get this self-contradictory message:
error[E0277]: `&impl futures::Future` is not a future
--> my/code:a:b
|
111 | let res_a = futures.get("a").expect("unreachable").await;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&impl futures::Future` is not a future
|
= help: the trait `futures::Future` is not implemented for `&impl futures::Future`
= note: required by `futures::Future::poll`
How can I await futures that I've put into a HashMap? Or is there another way altogether?
Using await requires the Future is pinned and mutable.
use std::collections::HashMap;
async fn my_async_fn() -> i64 {
return 0;
}
#[tokio::main]
async fn main() {
let mut futures = HashMap::new();
futures.insert("a", Box::pin(my_async_fn()));
// this is where I would do other work not blocked by these futures
let res_a = futures.get_mut("a").expect("unreachable").await;
println!("my result: {}", res_a);
}
I am trying to concurrently process arriving UDP packets in Tokio. However the following MWE does not do what I expected:
extern crate futures;
extern crate tokio_core;
extern crate tokio_io;
use futures::{Future, Stream};
use std::net::SocketAddr;
use tokio_core::net::{UdpCodec, UdpSocket};
use tokio_core::reactor::Core;
// just a codec to send and receive bytes
pub struct LineCodec;
impl UdpCodec for LineCodec {
type In = (SocketAddr, Vec<u8>);
type Out = (SocketAddr, Vec<u8>);
fn decode(&mut self, addr: &SocketAddr, buf: &[u8]) -> std::io::Result<Self::In> {
Ok((*addr, buf.to_vec()))
}
fn encode(&mut self, (addr, buf): Self::Out, into: &mut Vec<u8>) -> SocketAddr {
into.extend(buf);
addr
}
}
fn compute(addr: SocketAddr, msg: Vec<u8>) -> Box<Future<Item = (), Error = ()>> {
println!("Starting to compute for: {}", addr);
// sleep is a placeholder for a long computation
std::thread::sleep(std::time::Duration::from_secs(8));
println!("Done computing for for: {}", addr);
Box::new(futures::future::ok(()))
}
fn main() {
let mut core = Core::new().unwrap();
let handle = core.handle();
let listening_addr = "127.0.0.1:8080".parse::<SocketAddr>().unwrap();
let socket = UdpSocket::bind(&listening_addr, &handle).unwrap();
println!("Listening on: {}", socket.local_addr().unwrap());
let (writer, reader) = socket.framed(LineCodec).split();
let socket_read = reader.for_each(|(addr, msg)| {
println!("Got {:?}", msg);
handle.spawn(compute(addr, msg));
Ok(())
});
core.run(socket_read).unwrap();
}
Connecting two terminals with $ nc -u localhost 8080 and sending some text, I can see that the message from the second terminal is processed after the first finished.
What do I have to change?
As #Stefan said in another answer, you should not block in the asynchronous code. Given your example, it looks like the sleep is a placeholder for some long computation. So instead of using a timeout, you should delegate that computation to another thread like this example:
extern crate futures;
extern crate futures_cpupool;
use futures::Future;
use futures_cpupool::CpuPool;
...
let pool = CpuPool::new_num_cpus();
...
fn compute(handle: &Handle, addr: SocketAddr, _msg: Vec<u8>) -> Box<Future<Item = (), Error = ()>> {
// I don't know enough about Tokio to know how to make `pool` available here
pool.spawn_fn (|| {
println!("Starting to compute for: {}", addr);
std::thread::sleep(std::time::Duration::from_secs(8));
println!("Done computing for for: {}", addr);
Ok(())
})
}
Never sleep in async code (and avoid any other blocking calls too).
You might want to use Timeout instead like this:
Playground
fn compute(handle: &Handle, addr: SocketAddr, _msg: Vec<u8>) -> Box<Future<Item = (), Error = ()>> {
println!("Starting to compute for: {}", addr);
Box::new(
Timeout::new(std::time::Duration::from_secs(8), handle)
.unwrap()
.map_err(|e| panic!("timeout failed: {:?}", e))
.and_then(move |()| {
println!("Done computing for for: {}", addr);
Ok(())
}),
)
}
I have a concept project where the client sends a server a number (PrimeClientRequest), the server computes if the value is prime or not, and returns a response (PrimeClientResponse). I want the client to be a simple CLI which prompts the user for a number, sends the request to the server, and displays the response. Ideally I want to do this using TcpClient from Tokio and Streams from Futures-Rs.
I've written a Tokio server using services and I want to reuse the same codec and proto for the client.
Part of the client is a function called read_prompt which returns a Stream. Essentially it is an infinite loop at which each iteration reads in some input from stdin.
Here's the relevant code:
main.rs
use futures::{Future, Stream};
use std::env;
use std::net::SocketAddr;
use tokio_core::reactor::Core;
use tokio_prime::protocol::PrimeClientProto;
use tokio_prime::request::PrimeRequest;
use tokio_proto::TcpClient;
use tokio_service::Service;
mod cli;
fn main() {
let mut core = Core::new().unwrap();
let handle = core.handle();
let addr_string = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
let remote_addr = addr_string.parse::<SocketAddr>().unwrap();
println!("Connecting on {}", remote_addr);
let tcp_client = TcpClient::new(PrimeClientProto).connect(&remote_addr, &handle);
core.run(tcp_client.and_then(|client| {
client
.call(PrimeRequest { number: Ok(0) })
.and_then(|response| {
println!("RESP = {:?}", response);
Ok(())
})
})).unwrap();
}
cli.rs
use futures::{Future, Sink, Stream};
use futures::sync::mpsc;
use std::{io, thread};
use std::io::{Stdin, Stdout};
use std::io::prelude::*;
pub fn read_prompt() -> impl Stream<Item = u64, Error = ()> {
let (tx, rx) = mpsc::channel(1);
thread::spawn(move || loop {
let thread_tx = tx.clone();
let input = prompt(io::stdout(), io::stdin()).unwrap();
let parsed_input = input
.parse::<u64>()
.map_err(|_| io::Error::new(io::ErrorKind::Other, "invalid u64"));
thread_tx.send(parsed_input.unwrap()).wait().unwrap();
});
rx
}
fn prompt(stdout: Stdout, stdin: Stdin) -> io::Result<String> {
let mut stdout_handle = stdout.lock();
stdout_handle.write(b"> ")?;
stdout_handle.flush()?;
let mut buf = String::new();
let mut stdin_handle = stdin.lock();
stdin_handle.read_line(&mut buf)?;
Ok(buf.trim().to_string())
}
With the code above, the client sends a single request to the server before the client terminates. I want to be able to use the stream generated from read_prompt to provide input to the TcpClient and make a request per item in the stream. How would I go about doing this?
The full code can be found at joshleeb/tokio-prime.
The solution I have come up with (so far) has been to use the LoopFn in the Future-Rs crate. It's not ideal as a new connection still has to be made but it is at least a step in the right direction.
main.rs
use futures::{future, Future};
use std::{env, io};
use std::net::SocketAddr;
use tokio_core::reactor::{Core, Handle};
use tokio_prime::protocol::PrimeClientProto;
use tokio_prime::request::PrimeRequest;
use tokio_proto::TcpClient;
use tokio_service::Service;
mod cli;
fn handler<'a>(
handle: &'a Handle, addr: &'a SocketAddr
) -> impl Future<Item = (), Error = ()> + 'a {
cli::prompt(io::stdin(), io::stdout())
.and_then(move |number| {
TcpClient::new(PrimeClientProto)
.connect(addr, handle)
.and_then(move |client| Ok((client, number)))
})
.and_then(|(client, number)| {
client
.call(PrimeRequest { number: Ok(number) })
.and_then(|response| {
println!("{:?}", response);
Ok(())
})
})
.or_else(|err| {
println!("! {}", err);
Ok(())
})
}
fn main() {
let mut core = Core::new().unwrap();
let handle = core.handle();
let addr_string = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
let remote_addr = addr_string.parse::<SocketAddr>().unwrap();
println!("Connecting on {}", remote_addr);
let client = future::loop_fn((), |_| {
handler(&handle, &remote_addr)
.map(|_| -> future::Loop<(), ()> { future::Loop::Continue(()) })
});
core.run(client).ok();
}
cli.rs
use futures::prelude::*;
use std::io;
use std::io::{Stdin, Stdout};
use std::io::prelude::*;
#[async]
pub fn prompt(stdin: Stdin, stdout: Stdout) -> io::Result<u64> {
let mut stdout_handle = stdout.lock();
stdout_handle.write(b"> ")?;
stdout_handle.flush()?;
let mut buf = String::new();
let mut stdin_handle = stdin.lock();
stdin_handle.read_line(&mut buf)?;
parse_input(buf.trim().to_string())
}
fn parse_input(s: String) -> io::Result<u64> {
s.parse::<u64>()
.map_err(|_| io::Error::new(io::ErrorKind::Other, "invalid u64"))
}