about
11/25/2022
RustComm Repository
RustComm code

RustComm  Repository

Message-passing communicatior using TcpStream and TcpListener

Quick Status Code functions correctly No known defects Demonstration code yes Documentation yes Test cases yes Static library yes Build requires Rust installation Planned design changes Add file transfer support
Fig 1. RustComm Concept
Fig 2. RustComm Output

Concept:

RustComm is a facility for sending messages between a Sender and Receiver. It uses the std::net::TcpStream and std::net::TcpListener types.
This is a prototype for message-passing communication system. It provides three user defined types: Connector, Listener, and Message, with generic parameters M, P, and L, as shown in Fig. 1.
M implements the Msg trait and represents a message to be sent between endpoints. P implements the Process<M> trait that defines message processing, and L implements the Logger trait that supports logging events to the console that can be turned on or off by the types supplied for L, e.g., VerboseLog and MuteLog.
The RustComm library:
  • Uses queued full-duplex buffered message sending and receiving
  • Each message has a fixed size header and Vec<u8> body.
  • For each Connector<P, M, L> connection, Listener<P, L> processes messages until receiving a message with MessageType::END. Listener<P, L> spawns a thread for each client connection and processes messages in P::process_message.
  • In this version, P::process_message echos back message with "reply" appended as reply to sender. You observe that behavior in Fig. 2.
The long-term goal for RustComm is to serve as a prototyping platform for various messaging and processing strategies. This version defines traits: Sndr<M>, Rcvr<M>, Process<M>, Msg, and Logger. The user-defined types, M and P, are things that change as we change the message structure, defined by M and connector and listener processing defined by P. These types are defined in the rust_comm_processing crate. The somewhat complex handling of TcpStreams and TcpListener are expected to remain fixed. They are defined in the crate rust_comm. Finally, logger L provides a write method that will, using VerboseLog for L, write its argument to the console. MuteLog simply discards its argument. The last step in this phase of development is to add a threadpool, as shown in Fig. 1. The threadpool exists and has been lightly tested. What remains is to integrate it into the Listener component.

Current Design:

There are three user-defined types: Message, Connector, and Listener. Connector and Listener each use an existing component BlockingQueue<Message> Message: Methods:
  1. new() -> Message
    Create new Message with empty body and MessageType::TEXT.
  2. set_type(&mut self, mt: u8)
    Set MessageType member to one of: TEXT, BYTES, END.
  3. get_type(&self) -> MessageType
    Return MessageType member value.
  4. set_body_bytes(&mut self, b: Vec<u8>)
    Set body_buffer member to bytes fromb: Vec<u8>.
  5. set_body_str(&mut self, s: &str;)
    Set body_buffer member to bytes froms: &str.
  6. get_body_size(&self) -> usize
    Return size in bytes of body member.
  7. get_body(&self) -> &Vec<u8>
    Return body_buffer member.
  8. get_body_str(&self) -> String
    Return body contents as lossy String.
  9. clear(&self)
    clear body contents.

Both Connector<P, M, L> and Listener<P, L> are parameterized with L, a type satisfying a Logger trait. The package defines two types that implement the trait, VerboseLog and MuteLog that allow users to easily turn on and off event display outputs. Fig 2. uses MuteLog in both Connector<P, M, L> and Listener<P, L>.

Connector<P, M, L> methods:
  1. new(addr: &'static str) -> std::io::Result<Connector<P,M,L>>
    Create new Connector<P,M,L> with running send and receive threads.
  2. is_connected(&self) -> bool
    is connected to addr?.
  3. post_message(&self, msg: M)
    Enqueues msg to send to connected Receiver.
  4. get_message(&mut self) -> M
    Reads reply message if available, else blocks.
  5. has_message(&self) -> bool
    Returns true if reply message is available.
Listener<P, L> methods:
  1. new() -> Listener<P, L>
    Create new Listener<P, L>.
  2. start(&mut self, addr: &'static str) -> std::io::Result<JoinHandle<()>>
    Bind Listener<P,L> to addr and start listening on dedicated thread.

Operation:

This is intended to be a simple test-bed for ideas - easy to use and with very little setup and configuration.

Build:

Download and, in a command prompt, cargo build or cargo run.

Status:

Expected Changes and Additions:
  • Define traits Msg, Sndr, Rcvr, and Process. Refactor package code into Connector<C> and Listener<P>, where C and P implement the traits. The intent is to allow designers to create application specific processing, providing Listeners send and receive methods that are tailored to an application's messages and processing, that simply plug into the RustComm framework.
  •  Add reply messages to this demo. 
  • Add  Sender queue  and a threadpool in Receiver
  • Add file transfer capability
  •  Convert to buffered reads and writes 
  • Add user-defined Comm type that composes a Sender and a Receiver.
  • Support interchangeable Messages that use references to external facilities for defining message body contents rather than packing into message.
    Needed for file transfer so file blocks don't get copied into message and then, as part of message, get copied into stream. With that, we copy message header into stream followed by copy of file block into stream.
  Next Prev Pages Sections About Keys