Merge pull request #52 from tomaka/multistream-select-doc

Documentation for multistream-select
This commit is contained in:
Fredrik Harrysson
2017-12-01 12:59:10 +01:00
committed by GitHub
2 changed files with 187 additions and 10 deletions

View File

@ -0,0 +1,92 @@
# Multistream-select
This crate implements the `multistream-select` protocol, which is the protocol used by libp2p
to negotiate which protocol to use with the remote.
> **Note**: This crate is used by the internals of *libp2p*, and it is not required to
> understand it in order to use *libp2p*.
Whenever a new connection or a new multiplexed substream is opened, libp2p uses
`multistream-select` to negotiate with the remote which protocol to use. After a protocol has
been successfully negotiated, the stream (ie. the connection or the multiplexed substream)
immediately stops using `multistream-select` and starts using the negotiated protocol.
## Protocol explanation
The dialer has two options available: either request the list of protocols that the listener
supports, or suggest a protocol. If a protocol is suggested, the listener can either accept (by
answering with the same protocol name) or refuse the choice (by answering "not available").
## Examples
For a dialer:
```rust
extern crate bytes;
extern crate futures;
extern crate multistream_select;
extern crate tokio_core;
use bytes::Bytes;
use multistream_select::dialer_select_proto;
use futures::{Future, Sink, Stream};
use tokio_core::net::TcpStream;
use tokio_core::reactor::Core;
let mut core = Core::new().unwrap();
#[derive(Debug, Copy, Clone)]
enum MyProto { Echo, Hello }
let client = TcpStream::connect(&"127.0.0.1:10333".parse().unwrap(), &core.handle())
.from_err()
.and_then(move |connec| {
let protos = vec![
(Bytes::from("/echo/1.0.0"), <Bytes as PartialEq>::eq, MyProto::Echo),
(Bytes::from("/hello/2.5.0"), <Bytes as PartialEq>::eq, MyProto::Hello),
]
.into_iter();
dialer_select_proto(connec, protos).map(|r| r.0)
});
let negotiated_protocol: MyProto = core.run(client).expect("failed to find a protocol");
println!("negotiated: {:?}", negotiated_protocol);
```
For a listener:
```rust
extern crate bytes;
extern crate futures;
extern crate multistream_select;
extern crate tokio_core;
use bytes::Bytes;
use multistream_select::listener_select_proto;
use futures::{Future, Sink, Stream};
use tokio_core::net::TcpListener;
use tokio_core::reactor::Core;
let mut core = Core::new().unwrap();
#[derive(Debug, Copy, Clone)]
enum MyProto { Echo, Hello }
let server = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap()
.incoming()
.from_err()
.and_then(move |(connec, _)| {
let protos = vec![
(Bytes::from("/echo/1.0.0"), <Bytes as PartialEq>::eq, MyProto::Echo),
(Bytes::from("/hello/2.5.0"), <Bytes as PartialEq>::eq, MyProto::Hello),
]
.into_iter();
listener_select_proto(connec, protos)
})
.for_each(|(proto, _connec)| {
println!("new remote with {:?} negotiated", proto);
Ok(())
});
core.run(server).expect("failed to run server");
```

View File

@ -18,20 +18,105 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
// TODO: use this once stable ; for now we just copy-paste the content of the README.md
//#![doc(include = "../README.md")]
//! # Multistream-select
//!
//! Multistream-select is the "main" protocol of libp2p.
//! Whenever a connection opens between two peers, it starts talking in `multistream-select`.
//! This crate implements the `multistream-select` protocol, which is the protocol used by libp2p
//! to negotiate which protocol to use with the remote.
//!
//! The purpose of `multistream-select` is to choose which protocol we are going to use. As soon as
//! both sides agree on a given protocol, the socket immediately starts using it and multistream is
//! no longer relevant.
//! > **Note**: This crate is used by the internals of *libp2p*, and it is not required to
//! > understand it in order to use *libp2p*.
//!
//! However note that `multistream-select` is also sometimes used on top of another protocol such
//! as secio or multiplex. For example, two hosts can use `multistream-select` to decide to use
//! secio, then use `multistream-select` again (wrapped inside `secio`) to decide to use
//! `multiplex`, then use `multistream-select` one more time (wrapped inside `secio` and
//! `multiplex`) to decide to use the final actual protocol.
//! Whenever a new connection or a new multiplexed substream is opened, libp2p uses
//! `multistream-select` to negotiate with the remote which protocol to use. After a protocol has
//! been successfully negotiated, the stream (ie. the connection or the multiplexed substream)
//! immediately stops using `multistream-select` and starts using the negotiated protocol.
//!
//! ## Protocol explanation
//!
//! The dialer has two options available: either request the list of protocols that the listener
//! supports, or suggest a protocol. If a protocol is suggested, the listener can either accept (by
//! answering with the same protocol name) or refuse the choice (by answering "not available").
//!
//! ## Examples
//!
//! For a dialer:
//!
//! ```no_run
//! extern crate bytes;
//! extern crate futures;
//! extern crate multistream_select;
//! extern crate tokio_core;
//!
//! # fn main() {
//! use bytes::Bytes;
//! use multistream_select::dialer_select_proto;
//! use futures::{Future, Sink, Stream};
//! use tokio_core::net::TcpStream;
//! use tokio_core::reactor::Core;
//!
//! let mut core = Core::new().unwrap();
//!
//! #[derive(Debug, Copy, Clone)]
//! enum MyProto { Echo, Hello }
//!
//! let client = TcpStream::connect(&"127.0.0.1:10333".parse().unwrap(), &core.handle())
//! .from_err()
//! .and_then(move |connec| {
//! let protos = vec![
//! (Bytes::from("/echo/1.0.0"), <Bytes as PartialEq>::eq, MyProto::Echo),
//! (Bytes::from("/hello/2.5.0"), <Bytes as PartialEq>::eq, MyProto::Hello),
//! ]
//! .into_iter();
//! dialer_select_proto(connec, protos).map(|r| r.0)
//! });
//!
//! let negotiated_protocol: MyProto = core.run(client).expect("failed to find a protocol");
//! println!("negotiated: {:?}", negotiated_protocol);
//! # }
//! ```
//!
//! For a listener:
//!
//! ```no_run
//! extern crate bytes;
//! extern crate futures;
//! extern crate multistream_select;
//! extern crate tokio_core;
//!
//! # fn main() {
//! use bytes::Bytes;
//! use multistream_select::listener_select_proto;
//! use futures::{Future, Sink, Stream};
//! use tokio_core::net::TcpListener;
//! use tokio_core::reactor::Core;
//!
//! let mut core = Core::new().unwrap();
//!
//! #[derive(Debug, Copy, Clone)]
//! enum MyProto { Echo, Hello }
//!
//! let server = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap()
//! .incoming()
//! .from_err()
//! .and_then(move |(connec, _)| {
//! let protos = vec![
//! (Bytes::from("/echo/1.0.0"), <Bytes as PartialEq>::eq, MyProto::Echo),
//! (Bytes::from("/hello/2.5.0"), <Bytes as PartialEq>::eq, MyProto::Hello),
//! ]
//! .into_iter();
//! listener_select_proto(connec, protos)
//! })
//! .for_each(|(proto, _connec)| {
//! println!("new remote with {:?} negotiated", proto);
//! Ok(())
//! });
//!
//! core.run(server).expect("failed to run server");
//! # }
//! ```
extern crate bytes;
extern crate futures;