1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! Async I/O for ARQ connections
//!
//! This module contains:
//!
//! * The [ArqStream](struct.ArqStream.html) struct, which provides
//! I/O operations on open ARQ connections
//! * An immutable connection [information](struct.ConnectionInfo.html) struct
//! * Reasons why a connection will [fail](enum.ConnectionFailedReason.html)
//! to complete
//!
//! # Example
//!
//! ```no_run
//! use std::str;
//!
//! use futures::prelude::*;
//!
//! use ardop_interface::tnc::*;
//! use ardop_interface::arq::ArqStream;
//!
//! // Provide this method with an open ArqStream
//! async fn handle_connection(mut conn: ArqStream) {
//! let mut inp = &mut [0u8; 256];
//! conn.write_all(b"hello world!\n").await.expect("I/O Error");
//! let num_read = conn.read(inp).await.expect("I/O Error");
//! if num_read == 0 {
//! println!("Connection closed by peer (EOF).");
//! } else {
//! println!("Received: {}", str::from_utf8(&inp[0..num_read]).unwrap());
//! }
//! println!("Connection info: {}", &conn);
//! }
//! ```
//!
//! # Async I/O Interface
//!
//! The [ArqStream](struct.ArqStream.html) implements the
//! [`futures::io::AsyncRead`](https://docs.rs/futures/latest/futures/io/trait.AsyncRead.html)
//! and [`futures::io::AsyncWrite`](https://docs.rs/futures/latest/futures/io/trait.AsyncWrite.html)
//! traits. This makes the `ArqStream` behave like an asynchronous
//! `TcpStream`. Bytes written to the object will be sent as
//! payload data to the remote peer. Data sent from the peer will
//! be available for reading.
//!
//! `io::Error`s returned by the I/O methods include:
//!
//! * `std::io::ErrorKind::ConnectionReset`: Failure to communicate
//! with the local ARDOP TNC—perhaps its TCP connection has
//! closed.
//! * `std::io::ErrorKind::UnexpectedEof`: For writes to a closed
//! connection.
//!
//! Reads of a closed connection will return *zero* bytes of data.
//!
//! The `async close()` trait method will initiate a disconnect with
//! the remote peer. Any un-flushed data will be lost. You may also
//! initiate a disconnect by letting the `ArqStream` drop out of scope.
//! **Beware**, however, that this will block the calling thread
//! until the disconnect completes.
//!
//! # Half-Duplex Operation
//!
//! Radio links on a single frequency are *half-duplex*: Only one
//! station can transmit at a time. The ARDOP protocol prevents packet
//! collisions between two peers by designating one station as the
//! *sending* side (ISS) and the other as the *receiving* side (IRS).
//! The IRS can attempt to become the ISS, but this takes time.
//!
//! `ArqStream` relies on the `AUTOBREAK` behavior in the ARDOP TNC.
//! When the link becomes idle, and your station has data to send,
//! the TNC will automatically attempt to become the ISS and send
//! the data. This has important design implications for radio-friendly
//! protocols:
//!
//! * The remote peer will not see any of the data you enqueue
//! for transmission until it has finished sending.
//!
//! * Link turnovers take time. To maximize throughput, avoid protocol
//! designs which require lots of "overs."
//!
//! # Framing Devices
//!
//! In most non-trivial applications, it is desirable to turn streams
//! of bytes into higher-level *messages* for your application to
//! consume. Messages must always be read atomically, regardless of how
//! many "overs" are required to deliver the data. Never rely on
//! receiving an entire message in one `read()`—that's not how
//! streaming connections work.
//!
//! For higher-level protocols, you should implement a framer around
//! the raw `ArqStream`. Framers slice the incoming byte stream into
//! messages and perform the inverse for outgoing data. Framers can
//! also encode "native Rust" types into their serialized, wireline
//! representations.
//!
//! [`futures_codec`](https://docs.rs/futures_codec) provides a
//! framework for building async framers. Use of this crate is
//! demonstrated in the
//! [`echoserver`](https://github.com/cbs228/ardop_interface/blob/master/examples/echoserver/src/main.rs)
//! example. You can also write your own framers and codecs.
//!
//! # Monitoring Status of Transmissions
//!
//! Bytes written to this object for transmission go through three
//! distinct phases:
//!
//! 1. **Staged bytes**: Have been accepted by `ardop_interface`
//! but might not yet have been sent to the local TNC.
//!
//! 2. **Unacknowledged bytes**: Have been accepted by the local
//! ARDOP TNC but have not yet been `ACK`'d by the remote peer.
//!
//! 3. **Acknowledged bytes**: Have been successfully sent to, and
//! are `ACK`'d by, the remote peer.
//!
//! The `ArqStream` has methods for retrieving each of these byte
//! counts. There is also a method for retrieving the count of
//! received bytes.
//!
//! # Panics
//!
//! Using the disconnect-on-Drop behavior will panic if the async
//! runtime is the `LocalPool` from the `futures` crate. We
//! recommend the use of the `async_std` or `tokio` crate instead.
//! Alternatively, ensure to close the connection before letting
//! the `ArqStream` drop.
pub use ArqStream;
pub use ;
pub use ConnectionFailedReason;