Listening to several TCP streams


#1

I am making a simple demo where 3 clients (processes) can connect and communicate with each other with TCP (localhost with different ports).

I am facing the problem of managing several TCP connections. I want to send serialized objects (enum Message), for example using serde_json.
I imagine having a HashMap<SocketAddr, TcpStream> for each client. Each client will need to listen to new messages from each of its TcpStreams, as well as be able to write to those TcpStreams at arbitrary times.
So I thought about spawning a thread for each TcpStream to read objects and push them to some global queue. These threads will have to call blocking functions on the TcpStream. Either by using serde_json::StreamDeserializer or TcpStream::read(). However, I haven’t found any good way of terminating a thread while it is in a blocking call. For the most part, setting some shared signal variable is recommended. So this doesn’t seem possible.

I was told tokio may be a solution, but that it may not be worth the bother if I just want a small, fast prototype. However, I don’t see any simple solution to this.


#2

You can:

  1. Set timeout on the socket so that, eg, read() doesn’t hang forever. If you time out, check a shutdown flag and exit the thread.
  2. Put the socket into non-blocking mode and handle WouldBlock errors somehow. Check shutdown flag, sleep/yield, something else.

Under what condition do you want to terminate the thread? If you’re just exiting the process, then assuming those threads are daemon, they’ll just get torn down by the kernel. But I guess you have some other termination points?

Tokio would handle this better but your code will (likely) be a lot more complex.

If you’re familiar with, say, raw epoll API, you can use the mio crate to do async socket processing. It may be a bit easier than tokio, but that depends on how comfortable you’re with such system APIs.


#3

Thanks -

I didn’t think of epoll - that could work indeed. And I didn’t know that I could set the socket in nonblocking mode.

I forgot to say why I want to terminate the threads: It’s for when a TCP connection is terminated.
On second thought, for this simple application, maybe that is not necessary - I’m not sure yet. It could be nice though.


#4

https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.set_nonblocking

Right - tcp will notify you when remote side of the connection has terminated - your blocked read/write operations will unblock and return. Getting that notification is not guaranteed when a lossy/malfunctioning network is between the peer and you, but since you’re entirely on localhost then this isn’t an issue.


#5

I see, thanks!