I am trying to send a serialized struct over tcp to multiple machines. The tcp handler receives the serialized struct (String type) by a crossbeam channel from another thread.
My problem is that the rx.try_iter() will drain the crossbeam channel, and if more than one client is connected the clients can't receive the same struct. I tried moving the rx.try_iter() out of the individual handle_client() function, but couldn't achieve a good result.
Thanks for your time and help!
This is what I have so far:
(Server side)
use std::net::{TcpListener, TcpStream};
use std::thread;
use std::io::{Read,Write,Error};
use serde::{Serialize, Deserialize};
use crossbeam_channel::unbounded;
#[derive(Serialize, Deserialize)]
pub struct Serialized {
pub buffer: Vec<u32>,
pub timestep: u128,
}
impl Serialized {
pub fn serialize(buffer: Vec<u32>, timestep: u128) -> String {
let x = Serialized {
buffer,
timestep
};
serde_json::to_string(&x).unwrap()
}
}
fn handle_client(mut stream: TcpStream, rx: crossbeam_channel::Receiver<String>)-> Result<(), Error> {
println!("incoming connection from: {}", stream.peer_addr()?);
loop {
//receive from channel
let serialized = match rx.try_iter().last(){
Some(x) => x,
None => continue,
};
//write to stream
stream.write(serialized.as_bytes())?;
}
}
pub fn start_server(rx: crossbeam_channel::Receiver<String>) {
let listener = TcpListener::bind("localhost:8888").expect("Could not bind");
for stream in listener.incoming() {
let rx = rx.clone();
match stream {
Err(e)=> {eprintln!("failed: {}", e)}
Ok(stream) => {
thread::spawn(move || {
handle_client(stream, rx).unwrap_or_else(|error| eprintln!("{:?}", error));
});
}
}
}
}
(Client side)
use std::net::TcpStream;
use serde::{Serialize, Deserialize};
use std::error::Error as er;
#[derive(Serialize, Deserialize, Debug)]
pub struct Serialized {
pub buffer: Vec<u32>,
pub timestep: u128,
}
fn read_user_from_stream(tcp_stream: &mut TcpStream) -> Result<Serialized, Box<dyn er>> {
let mut de = serde_json::Deserializer::from_reader(tcp_stream);
let u = Serialized::deserialize(&mut de)?;
Ok(u)
}
pub fn start_client() {
loop {
let mut stream = TcpStream::connect("localhost:8888").expect("could not connect");
let serialized = read_user_from_stream(&mut stream).unwrap();
println!("timestep: {}", serialized.timestep);
}
}
fn main() {
start_client();
}
You can't use crossbeam to broadcast items. crossbeam only provides a producer-consumer architecture; if you want to deliver an item to multiple receivers, you need to use a different mechanism.
It seems that the bus crate provides what you need.
After some discussion on the rust user board, I came to this solution (server side):
use std::sync::{Arc, Mutex};
use std::thread;
use std::net::{TcpListener, TcpStream};
use std::io::{Read,Write,Error};
use bus::{Bus, BusReader};
fn main() {
let mut x: u32 = 0;
let bus = Bus::<u32>::new(10);
let bus_mutex = Arc::new(Mutex::new(bus));
let bus_mutex_cp = Arc::clone(&bus_mutex);
thread::spawn(move || {
start_server(bus_mutex_cp);
});
//simulation loop
for _ in 0..99999 {
x = x + 1;
println!("Simulation step: {}", x);
bus_mutex.lock().unwrap().broadcast(x);
thread::sleep_ms(1000);
}
loop {}
}
pub fn start_server(bus_mutex: Arc<Mutex<Bus<u32>>>) {
let listener = TcpListener::bind("0.0.0.0:8888").expect("Could not bind");
for stream in listener.incoming() {
match stream {
Err(e)=> {eprintln!("failed: {}", e)}
Ok(stream) => {
let rx = bus_mutex.lock().unwrap().add_rx();
thread::spawn(move || {
handle_client(stream, rx).unwrap_or_else(|error| eprintln!("{:?}", error));
});
}
}
}
}
fn handle_client(mut stream: TcpStream, mut rx: BusReader<u32>)-> Result<(), Error> {
println!("incoming connection from: {}", stream.peer_addr()?);
loop {
//receive from bus
let x = rx.recv().unwrap();
//write to stream
stream.write(&x.to_string().as_bytes())?;
thread::sleep_ms(100);
}
}
Related
The current issue that I am having is creating a way to reference the TCPListener from a thread into the Struct of the main thread. The end goal of what I am trying to accomplish is a way to reference the server's TCP connections from the Server struct.
Here is the code:
use std::io::Read;
use std::net::{TcpListener, TcpStream};
use std::sync::{Arc, mpsc, Mutex};
use std::thread;
fn main() {
let server = Server::start("127.0.0.1:25565".to_string());
loop {
for client in server.connected_clients {
println!("{:?}", client.stream.peer_addr().unwrap())
}
}
}
#[derive(Debug)]
struct Server {
listener: Arc<Mutex<TcpListener>>,
connected_clients: Vec<Client>,
}
impl Server {
pub fn start(address: String) -> Server {
let listener = TcpListener::bind(address).unwrap();
let (tx, rx) = mpsc::channel();
let listener = Arc::new(Mutex::new(listener));
let server = Server {
listener: listener,
connected_clients: Vec::new()
};
tx.send(&server.listener).unwrap();
thread::spawn(|| {
let listener = rx.recv().unwrap();
// For each new connection start a new thread
for stream in listener.lock().unwrap().incoming() {
let mut stream = stream.unwrap();
thread::spawn(move || {
// TODO: Add client to the connected_clients Vec
let mut buffer = [0; 1024];
loop {
stream.read(&mut buffer).unwrap();
println!("{}", String::from_utf8(Vec::from(&buffer[..])).unwrap().trim_end_matches(char::from(0)));
}
});
}
});
server
}
}
#[derive(Debug)]
struct Client {
id: usize,
stream: TcpStream,
}
ERROR: std::sync::mpsc::Receiver<&Arc<Mutex>>` cannot be shared between threads safely
I'm uncertain where you got the idea from to use channel to send an object to a thread you just spawned, but it's definitely incorrect in this case.
Just use a move || closure instead to move outer variables into it.
With that in mind, this code compiles: (although I have no way to test if it works)
use std::io::Read;
use std::net::{TcpListener, TcpStream};
use std::sync::{mpsc, Arc, Mutex};
use std::thread;
fn main() {
let server = Server::start("127.0.0.1:25565".to_string());
loop {
for client in &server.connected_clients {
println!("{:?}", client.stream.peer_addr().unwrap())
}
}
}
#[derive(Debug)]
struct Server {
listener: Arc<Mutex<TcpListener>>,
connected_clients: Vec<Client>,
}
impl Server {
pub fn start(address: String) -> Server {
let server = Server {
listener: Arc::new(Mutex::new(TcpListener::bind(address).unwrap())),
connected_clients: Vec::new(),
};
let listener = server.listener.clone();
thread::spawn(move || {
// For each new connection start a new thread
for stream in listener.lock().unwrap().incoming() {
let mut stream = stream.unwrap();
thread::spawn(move || {
// TODO: Add client to the connected_clients Vec
let mut buffer = [0; 1024];
loop {
stream.read(&mut buffer).unwrap();
println!(
"{}",
String::from_utf8(Vec::from(&buffer[..]))
.unwrap()
.trim_end_matches(char::from(0))
);
}
});
}
});
server
}
}
#[derive(Debug)]
struct Client {
id: usize,
stream: TcpStream,
}
Technically, while being complete overkill in this case, your channel approach would work as well, but then you have to send an actual instance of the object instead of a reference. And you still need to use a move || closure to move the rx object into it.
use std::io::Read;
use std::net::{TcpListener, TcpStream};
use std::sync::{mpsc, Arc, Mutex};
use std::thread;
fn main() {
let server = Server::start("127.0.0.1:25565".to_string());
loop {
for client in &server.connected_clients {
println!("{:?}", client.stream.peer_addr().unwrap())
}
}
}
#[derive(Debug)]
struct Server {
listener: Arc<Mutex<TcpListener>>,
connected_clients: Vec<Client>,
}
impl Server {
pub fn start(address: String) -> Server {
let listener = TcpListener::bind(address).unwrap();
let (tx, rx) = mpsc::channel();
let listener = Arc::new(Mutex::new(listener));
let server = Server {
listener: listener,
connected_clients: Vec::new(),
};
tx.send(server.listener.clone()).unwrap();
thread::spawn(move || {
let listener = rx.recv().unwrap();
// For each new connection start a new thread
for stream in listener.lock().unwrap().incoming() {
let mut stream = stream.unwrap();
thread::spawn(move || {
// TODO: Add client to the connected_clients Vec
let mut buffer = [0; 1024];
loop {
stream.read(&mut buffer).unwrap();
println!(
"{}",
String::from_utf8(Vec::from(&buffer[..]))
.unwrap()
.trim_end_matches(char::from(0))
);
}
});
}
});
server
}
}
#[derive(Debug)]
struct Client {
id: usize,
stream: TcpStream,
}
One last remark:
I question the usefulness of the entire construct, though. As soon as your thread is inside the for loop, your listener is continuously locked, meaning, whoever tries to actually access it through your Server object will simply deadlock.
I found this script in the post Recommended way of IPC in Rust where a server and client are created with named pipes.
I want to understand how it works so I started debugging. When I start the server with cargo run listen, the program reaches the open function and the following happens. I know this is a feature and not a bug, but I do not understand why it happens.
In the main function the listen function is called and then the listen function calls the open function:
use libc::{c_char, mkfifo};
use serde::{Deserialize, Serialize};
use std::env::args;
use std::fs::{File, OpenOptions};
use std::io::{Error, Read, Result, Write};
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};
fn main() -> Result<()> {
let mut args = args();
let _ = args.next();
match args.next().as_ref().map(String::as_str) {
Some("listen") => listen()?,
Some("send") => {
let msg = args.next().unwrap();
send(msg)?;
}
_ => {
eprintln!("Please either listen or send.");
}
}
Ok(())
}
pub struct Fifo {
path: PathBuf,
}
impl Fifo {
pub fn new(path: PathBuf) -> Result<Self> {
let os_str = path.clone().into_os_string();
let slice = os_str.as_bytes();
let mut bytes = Vec::with_capacity(slice.len() + 1);
bytes.extend_from_slice(slice);
bytes.push(0); // zero terminated string
let _ = std::fs::remove_file(&path);
if unsafe { mkfifo((&bytes[0]) as *const u8 as *const c_char, 0o644) } != 0 {
Err(Error::last_os_error())
} else {
Ok(Fifo { path })
}
}
/// Blocks until anyone connects to this fifo.
pub fn open(&self) -> Result<FifoHandle> {
let mut pipe = OpenOptions::new().read(true).open(&self.path)?;
let mut pid_bytes = [0u8; 4];
pipe.read_exact(&mut pid_bytes)?;
let pid = u32::from_ne_bytes(pid_bytes);
drop(pipe);
let read = OpenOptions::new()
.read(true)
.open(format!("/tmp/rust-fifo-read.{}", pid))?;
let write = OpenOptions::new()
.write(true)
.open(format!("/tmp/rust-fifo-write.{}", pid))?;
Ok(FifoHandle { read, write })
}
}
impl Drop for Fifo {
fn drop(&mut self) {
let _ = std::fs::remove_file(&self.path);
}
}
#[derive(Serialize, Deserialize)]
pub enum Message {
Print(String),
Ack(),
}
pub struct FifoHandle {
read: File,
write: File,
}
impl FifoHandle {
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
let pid = std::process::id();
let read_fifo_path = format!("/tmp/rust-fifo-write.{}", pid);
let read_fifo = Fifo::new(read_fifo_path.into())?;
let write_fifo_path = format!("/tmp/rust-fifo-read.{}", pid);
let write_fifo = Fifo::new(write_fifo_path.into())?;
let mut pipe = OpenOptions::new().write(true).open(path.as_ref())?;
let pid_bytes: [u8; 4] = u32::to_ne_bytes(pid);
pipe.write_all(&pid_bytes)?;
pipe.flush()?;
let write = OpenOptions::new().write(true).open(&write_fifo.path)?;
let read = OpenOptions::new().read(true).open(&read_fifo.path)?;
Ok(Self { read, write })
}
pub fn send_message(&mut self, msg: &Message) -> Result<()> {
let msg = bincode::serialize(msg).expect("Serialization failed");
self.write.write_all(&usize::to_ne_bytes(msg.len()))?;
self.write.write_all(&msg[..])?;
self.write.flush()
}
pub fn recv_message(&mut self) -> Result<Message> {
let mut len_bytes = [0u8; std::mem::size_of::<usize>()];
self.read.read_exact(&mut len_bytes)?;
let len = usize::from_ne_bytes(len_bytes);
let mut buf = vec![0; len];
self.read.read_exact(&mut buf[..])?;
Ok(bincode::deserialize(&buf[..]).expect("Deserialization failed"))
}
}
fn listen() -> Result<()> {
let fifo = Fifo::new(PathBuf::from("/tmp/rust-fifo"))?;
loop {
let mut handle = fifo.open()?;
std::thread::spawn(move || {
match handle.recv_message().expect("Failed to recieve message") {
Message::Print(p) => println!("{}", p),
Message::Ack() => panic!("Didn't expect Ack now."),
}
#[allow(deprecated)]
std::thread::sleep_ms(1000);
handle
.send_message(&Message::Ack())
.expect("Send message failed.");
});
}
}
fn send(s: String) -> Result<()> {
let mut handle = FifoHandle::open("/tmp/rust-fifo")?;
#[allow(deprecated)]
std::thread::sleep_ms(1000);
handle.send_message(&Message::Print(s))?;
match handle.recv_message()? {
Message::Print(p) => println!("{}", p),
Message::Ack() => {}
}
Ok(())
}
I'm trying to accept a UDP message but only if it happens within 5 seconds, I have a Stream abstraction built with both manually implementing Stream and by using the combinators in the futures library. Each way, after the recv_from future resolves, the duration will expire and the stream will return an Err(Elapsed(())). This is not the expected behaviour, if a value is returned, no error is supposed to be returned.
The expected behaviour is that the stream will resolve either the timeout or the Vec, but not one, then the other 5 seconds later.
use futures::{pin_mut, ready, stream::unfold, FutureExt};
use tokio::{
net::{udp, UdpSocket},
stream::{Stream, StreamExt},
time::{self, Duration},
};
use std::{
io,
net::SocketAddr,
pin::Pin,
task::{Context, Poll},
};
#[derive(Debug)]
pub(crate) struct UdpStream {
stream: udp::RecvHalf,
}
impl UdpStream {
fn new(stream: udp::RecvHalf) -> Self {
Self { stream }
}
fn stream(self) -> impl Stream<Item = io::Result<(Vec<u8>, SocketAddr)>> {
unfold(self.stream, |mut stream| async move {
let mut buf = [0; 4096];
match time::timeout(Duration::from_secs(5), stream.recv_from(&mut buf)).await {
Ok(Ok((len, src))) => {
Some((Ok((buf.iter().take(len).cloned().collect(), src)), stream))
}
e => {
println!("{:?}", e);
None
}
}
})
}
}
impl Stream for UdpStream {
type Item = io::Result<(Vec<u8>, SocketAddr)>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let socket = &mut self.stream;
pin_mut!(socket);
let mut buf = [0u8; 4096];
let (len, src) = ready!(Box::pin(socket.recv_from(&mut buf)).poll_unpin(cx))?;
Poll::Ready(Some(Ok((buf.iter().take(len).cloned().collect(), src))))
}
}
async fn listen_udp(addr: SocketAddr) -> io::Result<()> {
let udp = UdpSocket::bind(addr).await?;
let (mut udp_recv, mut udp_send) = udp.split();
let mut msg_stream = Box::pin(UdpStream::new(udp_recv).stream());
// use the manually implemented stream with this:
// let mut msg_stream = UdpStream::new(udp_recv).timeout(Duration::from_secs(5));
while let Some(msg) = msg_stream.next().await {
match msg {
Ok((buf, src)) => {
udp_send.send_to(&buf, &src).await?;
println!("Message recv: {:?}", buf);
}
Err(e) => {
eprintln!("timed out: {:?}", e);
}
}
}
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
listen_udp("127.0.0.1:9953".parse()?).await?;
Ok(())
}
You can try by running this code and making udp requests with echo "foo" | nc 127.0.0.1 9953 -u or with dig
cargo.toml
[package]
name = "udp_test"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tokio = { version = "0.2", features = ["full"] }
futures = "0.3"
After your stream returns the first (and the only) element, it goes back to wait for the next; it never ends until the timeout.
Basically a stream abstraction is not necessary here. A future wrapped in a timeout will do:
use std::{io, net::SocketAddr};
use tokio::{
net::UdpSocket,
time::{self, Duration},
};
async fn listen_udp(addr: SocketAddr) -> io::Result<()> {
let mut udp = UdpSocket::bind(addr).await?;
let mut buf = [0; 4096];
match time::timeout(Duration::from_secs(5), udp.recv_from(&mut buf)).await? {
Ok((count, src)) => {
udp.send_to(&buf[..count], &src).await?;
println!("Message recv: {:?}", &buf[..count]);
}
Err(e) => {
eprintln!("timed out: {:?}", e);
}
}
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
listen_udp("127.0.0.1:9953".parse()?).await?;
Ok(())
}
This question already has answers here:
How do I share a mutable object between threads using Arc?
(1 answer)
Sharing mutable self between multiple threads
(1 answer)
Closed 4 years ago.
I am writing an application which should read from a serial port in a loop (like a watcher) and also write commands to it.
The main thread is allowed to only write while the created thread can only read.
I created a simple example to reproduce my problem here. tx_thread reads from the serial port in a loop and on a certain condition it sends a message via MPSC channel. rx_thread looks for messages; when there is anything available it processes and it should also mutate the current state of the struct.
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
// this will also implement Drop trait to wait threads to
// be finished (message will be Enum instead of number in this case)
#[derive(Debug)]
struct MyStruct {
num: u32,
tx_thread: Option<thread::JoinHandle<()>>,
rx_thread: Option<thread::JoinHandle<()>>,
}
impl MyStruct {
fn new() -> MyStruct {
MyStruct {
num: 0,
tx_thread: None,
rx_thread: None,
}
}
fn start(&mut self) {
let (tx, rx) = mpsc::channel();
// tx thread will read from serial port infinitely,
// and send data to mpsc channel after certain condition
// to be processed.
let tx_thread = thread::spawn(move || {
let mut i = 0;
loop {
tx.send(i).unwrap();
i += 1;
thread::sleep(Duration::from_secs(1));
}
});
// after this will receive message, it will start
// processing and mutate `self` state if needed.
let rx_thread = thread::spawn(move || loop {
let num = rx.recv().unwrap();
println!("{:?}", num);
/* here, how do I save `num` to `self`? */
thread::sleep(Duration::from_secs(1));
});
self.tx_thread = Some(tx_thread);
self.rx_thread = Some(rx_thread);
}
}
fn main() {
let mut s = MyStruct::new();
s.start();
thread::sleep(Duration::from_secs(999999));
}
One amazing guy (Broken pen) on discord channel told me pretty much great solution to this, all credits to him.
So solution is to put properties that we want to be mutated in Arc<Mutex<>> instead and move cloned reference into the thread. So basically code will look like this:
use std::sync::{mpsc, Arc, Mutex};
use std::thread;
use std::time::Duration;
type MyType = Arc<Mutex<u32>>;
#[derive(Debug)]
struct MyStruct {
num: MyType,
tx_thread: Option<thread::JoinHandle<()>>,
rx_thread: Option<thread::JoinHandle<()>>,
}
impl MyStruct {
fn new() -> MyStruct {
MyStruct {
num: Arc::new(Mutex::new(0)),
tx_thread: None,
rx_thread: None,
}
}
fn start(&mut self) {
let (tx, rx) = mpsc::channel();
// tx thread will read from serial port infinitely,
// and send data to mpsc channel after certain condition
// to be processed.
let tx_thread = thread::spawn(move || {
let mut i = 0;
loop {
tx.send(i).unwrap();
i += 1;
thread::sleep(Duration::from_secs(1));
}
});
// clone here.
let arc_num = self.num.clone();
let rx_thread = thread::spawn(move || loop {
let num = rx.recv().unwrap();
// println!("{:?}", num);
// now we can use it for writing/reading.
*arc_num.lock().unwrap() = num;
println!("{:?}", *arc_num.lock().unwrap());
thread::sleep(Duration::from_secs(1));
});
self.tx_thread = Some(tx_thread);
self.rx_thread = Some(rx_thread);
}
}
fn main() {
let mut s = MyStruct::new();
s.start();
thread::sleep(Duration::from_secs(999999));
}
EDIT: another solution is to create inner struct with Arc<Mutex<>> and do work there, which gives you access to everything you need.
See code below:
use std::default::Default;
use std::sync::{mpsc, Arc, Mutex};
use std::thread;
use std::time::Duration;
// this will also implement Drop trait to wait threads to
// be finished (message will be Enum instead of number in this case)
#[derive(Debug, Default)]
struct MyStructInner {
num: u32,
tx_thread: Option<thread::JoinHandle<()>>,
rx_thread: Option<thread::JoinHandle<()>>,
}
#[derive(Debug, Default)]
struct MyStruct {
inner: Arc<Mutex<MyStructInner>>,
}
impl MyStruct {
fn new() -> MyStruct {
MyStruct {
inner: Arc::new(Mutex::new(MyStructInner {
num: 0,
..Default::default()
})),
}
}
fn start(&mut self) {
let (tx, rx) = mpsc::channel();
// tx thread will read from serial port infinitely,
// and send data to mpsc channel after certain condition
// to be processed.
let tx_thread = thread::spawn(move || {
let mut i = 0;
loop {
tx.send(i).unwrap();
i += 1;
thread::sleep(Duration::from_secs(1));
}
});
// after this will receive message, it will start
// processing and mutate `self` state if needed.
let local_self = self.inner.clone();
let rx_thread = thread::spawn(move || loop {
let num = rx.recv().unwrap();
local_self.lock().unwrap().num = num;
println!("{:?}", local_self.lock().unwrap().num);
thread::sleep(Duration::from_secs(1));
});
self.inner.lock().unwrap().tx_thread = Some(tx_thread);
self.inner.lock().unwrap().rx_thread = Some(rx_thread);
}
}
fn main() {
let mut s = MyStruct::new();
s.start();
thread::sleep(Duration::from_secs(999999));
}
Basically, I'm making a program that's listening to a bunch of ports and that handles incoming packets in different ways. I decide to bundle this code into a Trait:
use std::old_io::{TcpStream, TcpListener, Listener, Acceptor, EndOfFile, IoResult};
use std::thread::Thread;
trait Server {
fn new(port: u16) -> Self;
fn hostname(&self) -> &String;
fn initialize(&self) {
let acceptor = TcpListener::bind(self.hostname().as_slice()).listen().unwrap();
Thread::spawn(move|| {
let mut acceptor = acceptor;
for incoming_stream in acceptor.incoming() {
match incoming_stream {
Ok(stream) => {
self.handle_client(stream);
},
Err(ref e) if e.kind == EndOfFile => break,
Err(e) => panic!("Unexpected error: {}", e),
}
}
});
}
fn handle_client(&self, stream: TcpStream) -> ();
}
pub struct InternodeServer {
hostname: String,
}
impl Server for InternodeServer {
fn new(port: u16) -> InternodeServer {
let hostname = format!("127.0.0.1:{}", port);
InternodeServer {
hostname: hostname,
}
}
fn hostname(&self) -> &String {
&self.hostname
}
fn handle_client(&self, stream: TcpStream) {
println!("Received connection");
let mut stream = stream;
let response = b"Hello\r\n";
let _ = stream.write_all(response);
let _ = stream.close_write();
}
}
fn main() {
let test_server = <InternodeServer as Server>::new(9337);
test_server.initialize();
}
However, this code won't work because you can't send Self. This is the error I receive:
test.rs:11:9: 11:22 error: the trait `core::marker::Send` is not implemented for the type `Self` [E0277]
test.rs:11 Thread::spawn(move|| {
^~~~~~~~~~~~~
test.rs:11:9: 11:22 note: `Self` cannot be sent between threads safely
test.rs:11 Thread::spawn(move|| {
^~~~~~~~~~~~~
So I also tried making handle_client a static method to avoid self. To do this, I simply changed handle_client to:
fn handle_client(stream: TcpStream)
And referenced it by doing:
Server::handle_client(stream);
However, I can't reference InternodeServer's static methods from Server's initialize method. When compiling, I get an error like:
test.rs:16:25: 16:46 error: type annotations required: cannot resolve `_ : Server` [E0283]
test.rs:16 Server::handle_client(stream);
^~~~~~~~~~~~~~~~~~~~~
test.rs:16:25: 16:46 note: required by `Server::handle_client`
test.rs:16 Server::handle_client(stream);
Is there any way around this?
Here's a smaller reproduction of the error:
use std::thread::Thread;
trait Server {
fn initialize(&self) {
Thread::spawn(move || self.handle_client());
}
fn handle_client(&self);
}
fn main() {}
The problem is that the argument passed to Thread::spawn must be Send. You are trying to move self into the closure, but your trait doesn't guarantee Send, so the closure can't be Send.
We can attempt to go down that path with trait Server: Send, but then we get "cannot infer an appropriate lifetime" errors because Send also requires 'static (for now). Also, it seems very strange to move yourself into a closure.
Really, I think you want to split up your code. Move handle_client into a separate trait and then ensure that implementations of that trait are Send:
use std::thread::Thread;
trait Server {
fn initialize<D>(&self, driver: D)
where D: Driver + Send
{
Thread::spawn(move || driver.handle_client());
}
}
trait Driver {
fn handle_client(&self);
}
fn main() {}
I don't think that rust will allow you to invoke object methods directly from other thread because "move" closures cannot borrow anything, only move.
So you have to use some kind of inter-thread communication tool, for example, channels:
use std::thread::Thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Sender, Receiver, RecvError};
use std::net::{TcpStream, TcpListener};
use std::io::{ErrorKind, Write};
trait Server {
fn new(port: u16) -> Self;
fn hostname(&self) -> &String;
fn initialize(&mut self, _detached: bool) {
let acceptor = TcpListener::bind(self.hostname().as_slice()).unwrap();
let server_tx = self.make_pipe();
Thread::spawn(move|| {
for incoming_stream in acceptor.incoming() {
match incoming_stream {
Ok(stream) => server_tx.send(Arc::new(Mutex::new(stream))).unwrap(),
Err(ref e) if e.kind() == ErrorKind::NotConnected => break,
Err(e) => panic!("Unexpected error: {}", e),
}
}
});
}
fn handle_client(&self, stream: Arc<Mutex<TcpStream>>);
fn make_pipe(&mut self) -> Sender<Arc<Mutex<TcpStream>>>;
fn run(&self);
}
pub struct InternodeServer {
hostname: String,
client_rx: Option<Receiver<Arc<Mutex<TcpStream>>>>,
}
impl Server for InternodeServer {
fn new(port: u16) -> InternodeServer {
let hostname = format!("127.0.0.1:{}", port);
InternodeServer {
hostname: hostname,
client_rx: None,
}
}
fn make_pipe(&mut self) -> Sender<Arc<Mutex<TcpStream>>> {
let (server_tx, client_rx) = channel();
self.client_rx = Some(client_rx);
server_tx
}
fn hostname(&self) -> &String {
&self.hostname
}
fn handle_client(&self, stream_arc: Arc<Mutex<TcpStream>>) {
println!("Received connection");
let mut stream = stream_arc.lock().unwrap();
let response = b"Hello\r\n";
let _ = stream.write_all(response);
let _ = drop(stream);
}
fn run(&self) {
loop {
match self.client_rx.as_ref().unwrap().recv() {
Ok(stream) => self.handle_client(stream),
Err(RecvError) => break,
}
}
}
}
fn main() {
let mut s = <InternodeServer as Server>::new(10101);
s.initialize(false);
s.run();
}