I am writing a client for a Unix Domain Socket, which should listen for messages from a server, I got it to work after hours of researching (it shows 1-2 messages), but after that tokio crashes with the error: Error: Kind(WouldBlock), here is my code:
use std::env::{var, VarError};
use std::io::{self, Write};
use tokio::net::UnixStream;
#[tokio::main]
async fn main() -> io::Result<()> {
let hypr_instance_sig = match var("HYPRLAND_INSTANCE_SIGNATURE") {
Ok(var) => var,
Err(VarError::NotPresent) => panic!("Is hyprland running?"),
Err(VarError::NotUnicode(_)) => panic!("wtf no unicode?"),
};
let socket_path = format!("/tmp/hypr/{hypr_instance_sig}/.socket2.sock");
let stream = UnixStream::connect(socket_path).await?;
loop {
stream.readable().await?;
let mut buf = [0; 4096];
stream.try_read(&mut buf)?;
io::stdout().lock().write_all(&buf)?;
}
}
Could someone please help me?
I fully agree with #Caesar's comment.
don't use try_read, use read instead
slice the buffer to the correct size after reading
check for 0 bytes read, which indicates that the end of the stream was reached
use std::env::{var, VarError};
use std::io::{self, Write};
use tokio::io::AsyncReadExt;
use tokio::net::UnixStream;
#[tokio::main]
async fn main() -> io::Result<()> {
let hypr_instance_sig = match var("HYPRLAND_INSTANCE_SIGNATURE") {
Ok(var) => var,
Err(VarError::NotPresent) => panic!("Is hyprland running?"),
Err(VarError::NotUnicode(_)) => panic!("wtf no unicode?"),
};
let socket_path = format!("/tmp/hypr/{hypr_instance_sig}/.socket2.sock");
let mut stream = UnixStream::connect(socket_path).await?;
let mut buf = [0; 4096];
loop {
let num_read = stream.read(&mut buf).await?;
if num_read == 0 {
break;
}
let buf = &buf[..num_read];
io::stdout().lock().write_all(buf)?;
}
Ok(())
}
Disclaimer: I didn't test the code because I don't have the required socket file. It compiles, though.
Related
I want to make a chat server between my computer and a server that is running on replit but I can't connect to the replit server (it works when I run the server on my device so the code seems to work)
Here is the server-side code that runs on replit:
use std::io::{self, Read, Write};
use std::net::{TcpListener, TcpStream};
use std::str;
use std::thread;
fn handle_client(mut stream: TcpStream) -> std::io::Result<()> {
let mut data = [0; 4096];
loop {
let len = stream.read(&mut data)?;
let text = str::from_utf8(&data[..len]).unwrap();
println!("Received: {}", text);
println!("What do you want to send: ");
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
stream.write_all(input.as_bytes())?;
}
}
fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("0.0.0.0:6969")?;
for stream in listener.incoming() {
let stream = stream?;
println!("New connection: {}", stream.peer_addr()?);
thread::spawn(move || handle_client(stream));
}
Ok(())
}
And here is the client-side code:
use std::io::{self, Read, Write};
use std::net::TcpStream;
use std::str;
fn main() -> std::io::Result<()> {
let mut stream = TcpStream::connect("IncompleteHelpfulServer.smartflow.repl.co:6969")?;
loop {
println!("What do you want to send: ");
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
stream.write_all(input.as_bytes())?;
let mut data = [0; 4096];
let len = stream.read(&mut data)?;
let text = str::from_utf8(&data[..len]).unwrap();
println!("Recieved: {}", text);
}
}
When the server is running on replit and I try to connect to it I get this error:
Error: Custom { kind: Uncategorized, error: "Failed to lookup address information: Temporary failure in name resolution" }
I tried to change the address from IncompleteHelpfulServer.smartflow.repl.co:6969 to:
IncompleteHelpfulServer.smartflow.repl.co
https://IncompleteHelpfulServer.smartflow.repl.co:6969
http://IncompleteHelpfulServer.smartflow.repl.co:6969
https://IncompleteHelpfulServer.smartflow.repl.co
http://IncompleteHelpfulServer.smartflow.repl.co
But neither of them worked
I searched if it was even possible and it was also if I try to connect to the address with my browser the replit server gets some info about my browser so I think there is a problem in the client-side code
Can somebody help me fix this?
Thanks for reading
Managed to do it with hyper and native-tls with https
S.O to #Locke in the comments
I ahve a really simple tcp client/server setup that I found somewhere online.
use std::net::{Shutdown,TcpListener, TcpStream};
use std::thread;
use std::io::{Read,Write,Error};
fn handle_client(mut stream: TcpStream)-> Result<(), Error> {
println!("incoming connection from: {}", stream.peer_addr()?);
let mut buf = [0;512];
loop {
let bytes_read = stream.read(&mut buf)?;
if bytes_read == 0 {return Ok(())}
let tmp = format!("{}", String::from_utf8_lossy(&buf).trim());
eprintln!("getting {}",tmp);
stream.write(&vec![1,2,3,4])?;
}
}
fn main() {
let listener = TcpListener::bind("0.0.0.0:8888").expect("Could not bind");
let mut i = 0;
for stream in listener.incoming() {
match stream {
Err(e)=> {eprintln!("failed: {}", e)}
Ok(stream) => {
thread::spawn(move || {
handle_client(stream).unwrap_or_else(|error| eprintln!("{:?}", error));
});
}
}
}
}
and the client looks like so:
use std::net::TcpStream;
use std::str;
use std::io::{self,BufRead,BufReader,Write};
fn main() {
let mut stream = TcpStream::connect("0.0.0.0:8888").expect("could not connect");
loop {
let mut input = String::new();
let mut buffer : Vec<u8> = Vec::new();
io::stdin().read_line(&mut input).expect("failed to read stdin");
stream.write(input.as_bytes()).expect("Failed to write to server");
let mut reader = BufReader::new(&stream);
reader.read_until(b'\n', &mut buffer).expect("Could not read into buffer");
println!("{}", str::from_utf8(&buffer).expect("msg: &str"))
}
println!("Hello, world!");
}
(actually vs code tells me that the code inside the loop here is unreachable, which is false)
But this basically just lets the client send a message to the server, that the server sends back.
I would actually like to send vectors back and forth, which actually seems like a simpler task.
So, I change the line in the server that writes to the stream to this:
stream.write(&vec![1,2,3,4])?;
and now, in the client that prints the message it gets from the serve to:
println!("{:?}", &buffer)
But when I do this, nothing happens on the clientside, nothing is printed, and it seems like the loop is just stuck somewhere.
I gugess it has something to do with this line:
reader.read_until(b'\n', &mut buffer).expect("Could not read into buffer");
and I read some other way of reading in. I tried using a function from tcpstream called "read_vectored", but I can't use it at all for the stream object I have.
I use tokio::net::TcpStream to connect a small TCP server, I write a few bytes and expect to read the response from the server.
When I do that with the nc command, it works perfectly
[denis#docker-1 ~]$ echo "get" | nc 10.0.0.11 9090
[37e64dd7-91db-4c13-9f89-f1c87467ffb3][processed]
and the server logs show
Incoming peer instructions.
Waiting for peer instructions...
Reading bytes...
Got a few bytes [4]
Got a few bytes [[103, 101, 116, 10, 0, ...]]
Reading bytes...
Got a few bytes [0]
Got a few bytes [[0, 0, 0, 0, 0, 0,...]]
Writing some data back from peer : [37e64dd7-91db-4c13-9f89-f1c87467ffb3]
But from my Rust client, I can write the bytes but as soon as I want to read the data from the server, everything is locked (even the write action)
use std::collections::HashMap;
use std::ops::DerefMut;
use tokio::io;
use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use uuid::Uuid;
use std::sync::RwLock;
use lazy_static::*;
#[tokio::main]
async fn main() {
let data = "set".to_string();
let mut stream = TcpStream::connect("10.0.0.11:9090").await.unwrap();
let ( mut read, mut write) = tokio::io::split(stream);
let u2 = data.as_bytes();
write.write_all(u2).await.unwrap();
let mut msg : [u8;1024] = [0;1024];
let _response_size = read.read(&mut msg).await.unwrap();
println!("GOT = {:?}", msg);
}
When looking at the server logs (see below), it reads the 3 bytes sent by the client, but then it is not able to read further, waiting to detect there is 0 byte left to read.
Incoming peer instructions.
Waiting for peer instructions...
Reading bytes...
Got a few bytes [3]
Got a few bytes [[115, 101, 116, 0, 0, ...]]
Reading bytes...
Here is the server code
use std::collections::HashMap;
use std::ops::DerefMut;
use tokio::io;
use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use uuid::Uuid;
use std::sync::RwLock;
use lazy_static::*;
struct DataPool {
data : [u8;1024],
size : usize,
}
async fn whirl_socket( socket : &mut TcpStream ) -> Vec<DataPool> {
let mut pool: Vec<DataPool> = vec![];
let mut buf = [0; 1024];
// In a loop, read data from the socket until finished
loop {
println!("Reading bytes...");
buf = [0; 1024];
let n = match socket.read(&mut buf).await {
Ok(n) => n,
Err(e) => {
eprintln!("failed to read from socket; err = {:?}", e);
break;
}
};
println!("Got a few bytes [{}]", n);
println!("Got a few bytes [{:?}]", &buf);
pool.push(DataPool {
data: buf,
size: n,
});
if n == 0 {
break;
}
}
pool
}
async fn launch_server_listener() -> io::Result<()> {
println!("Listen to 9090...");
let listener = TcpListener::bind("10.0.0.11:9090").await?;
loop {
println!("Waiting for peer instructions...");
let (mut socket, _) = listener.accept().await?;
println!("Incoming peer instructions.");
tokio::spawn(async move {
let mut pool= whirl_socket(&mut socket).await;
let my_uuid = Uuid::new_v4();
// Write the data back
println!("Writing some data back from peer : [{}]", my_uuid);
let s = format!( "[{}][processed]\n", my_uuid.to_string());
let u = s.as_bytes();
if let Err(e) = socket.write_all(u).await {
eprintln!("failed to write to socket; err = {:?}", e);
return;
}
});
}
}
async fn start_servers() -> Result<(), Box<dyn std::error::Error>> {
let _r = tokio::join!(launch_server_listener());
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
start_servers().await?;
Ok(())
}
A read of 0 bytes means the read stream has closed. So in your client code you need to close the write stream. You can do this with .shutdown() from the AsyncWriteExt trait:
write.write_all(u2).await.unwrap();
write.shutdown().await.unwrap();
Because Rust does not have have the built-in ability to read from a file in a non-blocking manner, I have to spawn a thread which reads the file /dev/input/fs0 in order to get joystick events. Suppose the joystick is unused (nothing to read), so the reading thread is blocked while reading from the file.
Is there a way for the main thread to force the blocking read of the reading thread to resume, so the reading thread may exit cleanly?
In other languages, I would simply close the file in the main thread. This would force the blocking read to resume. But I have not found a way to do so in Rust, because reading requires a mutable reference to the file.
The idea is to call File::read only when there is available data. If there is no available data, we check a flag to see if the main thread requested to stop. If not, wait and try again.
Here is an example using nonblock crate:
extern crate nonblock;
use std::fs::File;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use nonblock::NonBlockingReader;
fn main() {
let f = File::open("/dev/stdin").expect("open failed");
let mut reader = NonBlockingReader::from_fd(f).expect("from_fd failed");
let exit = Arc::new(Mutex::new(false));
let texit = exit.clone();
println!("start reading, type something and enter");
thread::spawn(move || {
let mut buf: Vec<u8> = Vec::new();
while !*texit.lock().unwrap() {
let s = reader.read_available(&mut buf).expect("io error");
if s == 0 {
if reader.is_eof() {
println!("eof");
break;
}
} else {
println!("read {:?}", buf);
buf.clear();
}
thread::sleep(Duration::from_millis(200));
}
println!("stop reading");
});
thread::sleep(Duration::from_secs(5));
println!("closing file");
*exit.lock().unwrap() = true;
thread::sleep(Duration::from_secs(2));
println!("\"stop reading\" was printed before the main exit!");
}
fn read_async<F>(file: File, fun: F) -> thread::JoinHandle<()>
where F: Send + 'static + Fn(&Vec<u8>)
{
let mut reader = NonBlockingReader::from_fd(file).expect("from_fd failed");
let mut buf: Vec<u8> = Vec::new();
thread::spawn(move || {
loop {
let s = reader.read_available(&mut buf).expect("io error");
if s == 0 {
if reader.is_eof() {
break;
}
} else {
fun(&buf);
buf.clear();
}
thread::sleep(Duration::from_millis(100));
}
})
}
Here is an example using poll binding of nix crate. The function poll waits (with timeout) for specific events:
extern crate nix;
use std::io::Read;
use std::os::unix::io::AsRawFd;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use nix::poll;
fn main() {
let mut f = std::fs::File::open("/dev/stdin").expect("open failed");
let mut pfd = poll::PollFd {
fd: f.as_raw_fd(),
events: poll::POLLIN, // is there input data?
revents: poll::EventFlags::empty(),
};
let exit = Arc::new(Mutex::new(false));
let texit = exit.clone();
println!("start reading, type something and enter");
thread::spawn(move || {
let timeout = 100; // millisecs
let mut s = unsafe { std::slice::from_raw_parts_mut(&mut pfd, 1) };
let mut buffer = [0u8; 10];
loop {
if poll::poll(&mut s, timeout).expect("poll failed") != 0 {
let s = f.read(&mut buffer).expect("read failed");
println!("read {:?}", &buffer[..s]);
}
if *texit.lock().unwrap() {
break;
}
}
println!("stop reading");
});
thread::sleep(Duration::from_secs(5));
println!("closing file");
*exit.lock().unwrap() = true;
thread::sleep(Duration::from_secs(2));
println!("\"stop reading\" was printed before the main exit!");
}
I'm going to have multiple functions that all need access to one main socket.
Would it better to:
Pass this socket to each function that needs access to it
Have a globally accessible socket
Can someone provide an example of the best way to do this?
I come from a Python/Nim background where things like this are easily done.
Edit:
How can I pass a socket as an arg to a function being called within a thread.
Ex.
fn main() {
let mut s = BufferedStream::new((TcpStream::connect(server).unwrap()));
let thread = Thread::spawn(move || {
func1(s, arg1, arg2);
});
while true {
func2(s, arg1);
}
}
Answer for updated question
We can use TcpStream::try_clone:
use std::io::Read;
use std::net::{TcpStream, Shutdown};
use std::thread;
fn main() {
let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap();
let stream2 = stream.try_clone().unwrap();
let _t = thread::spawn(move || {
// close this stream after one second
thread::sleep_ms(1000);
stream2.shutdown(Shutdown::Read).unwrap();
});
// wait for some data, will get canceled after one second
let mut buf = [0];
stream.read(&mut buf).unwrap();
}
Original answer
It's usually (let's say 99.9% of the time) a bad idea to have any global mutable state, if you can help it. Just do as you said: pass the socket to the functions that need it.
use std::io::{self, Write};
use std::net::TcpStream;
fn send_name(stream: &mut TcpStream) -> io::Result<()> {
stream.write(&[42])?;
Ok(())
}
fn send_number(stream: &mut TcpStream) -> io::Result<()> {
stream.write(&[1, 2, 3])?;
Ok(())
}
fn main() {
let mut stream = TcpStream::connect("127.0.0.1:31337").unwrap();
let r = send_name(&mut stream).and_then(|_| send_number(&mut stream));
match r {
Ok(..) => println!("Yay, sent!"),
Err(e) => println!("Boom! {}", e),
}
}
You could also pass the TcpStream to a struct that manages it, and thus gives you a place to put similar methods.
use std::io::{self, Write};
use std::net::TcpStream;
struct GameService {
stream: TcpStream,
}
impl GameService {
fn send_name(&mut self) -> io::Result<()> {
self.stream.write(&[42])?;
Ok(())
}
fn send_number(&mut self) -> io::Result<()> {
self.stream.write(&[1, 2, 3])?;
Ok(())
}
}
fn main() {
let stream = TcpStream::connect("127.0.0.1:31337").unwrap();
let mut service = GameService { stream: stream };
let r = service.send_name().and_then(|_| service.send_number());
match r {
Ok(..) => println!("Yay, sent!"),
Err(e) => println!("Boom! {}", e),
}
}
None of this is really Rust-specific, these are generally-applicable programming practices.