25 unstable releases

0.13.0 Jun 20, 2023
0.12.1 Nov 25, 2022
0.12.0 Oct 14, 2022
0.11.0 Jan 27, 2022
0.4.0 Mar 29, 2019

#1467 in Network programming

Download history 56545/week @ 2023-11-20 54701/week @ 2023-11-27 49519/week @ 2023-12-04 46394/week @ 2023-12-11 41922/week @ 2023-12-18 18845/week @ 2023-12-25 31486/week @ 2024-01-01 52028/week @ 2024-01-08 66297/week @ 2024-01-15 62131/week @ 2024-01-22 72838/week @ 2024-01-29 76025/week @ 2024-02-05 75057/week @ 2024-02-12 91922/week @ 2024-02-19 89877/week @ 2024-02-26 83475/week @ 2024-03-04

344,023 downloads per month
Used in 354 crates (4 directly)

MIT license

83KB
1.5K SLoC

Multistream-select Protocol Negotiation

This crate implements the multistream-select protocol, which is the protocol used by libp2p to negotiate which application-layer protocol to use with the remote on a connection or substream.

Note: This crate is used primarily by core components of libp2p and it is usually not used directly on its own.

Roles

Two peers using the multistream-select negotiation protocol on an I/O stream are distinguished by their role as a dialer (or initiator) or as a listener (or responder). Thereby the dialer plays the active part, driving the protocol, whereas the listener reacts to the messages received.

The dialer has two options: it can either pick a protocol from the complete list of protocols that the listener supports, or it can directly suggest a protocol. Either way, a selected protocol is sent to the listener who can either accept (by echoing the same protocol) or reject (by responding with a message stating "not available"). If a suggested protocol is not available, the dialer may suggest another protocol. This process continues until a protocol is agreed upon, yielding a Negotiated stream, or the dialer has run out of alternatives.

See dialer_select_proto and listener_select_proto.

Negotiated

A Negotiated represents an I/O stream that has settled on a protocol to use. By default, with Version::V1, protocol negotiation is always at least one dedicated round-trip message exchange, before application data for the negotiated protocol can be sent by the dialer. There is a variant Version::V1Lazy that permits 0-RTT negotiation if the dialer only supports a single protocol. In that case, when a dialer settles on a protocol to use, the DialerSelectFuture yields a Negotiated I/O stream before the negotiation data has been flushed. It is then expecting confirmation for that protocol as the first messages read from the stream. This behaviour allows the dialer to immediately send data relating to the negotiated protocol together with the remaining negotiation message(s). Note, however, that a dialer that performs multiple 0-RTT negotiations in sequence for different protocols layered on top of each other may trigger undesirable behaviour for a listener not supporting one of the intermediate protocols. See dialer_select_proto and the documentation of Version::V1Lazy for further details.

Examples

For a dialer:

use async_std::net::TcpStream;
use multistream_select::{dialer_select_proto, Version};
use futures::prelude::*;

async_std::task::block_on(async move {
    let socket = TcpStream::connect("127.0.0.1:10333").await.unwrap();

    let protos = vec!["/echo/1.0.0", "/echo/2.5.0"];
    let (protocol, _io) = dialer_select_proto(socket, protos, Version::V1).await.unwrap();

    println!("Negotiated protocol: {:?}", protocol);
    // You can now use `_io` to communicate with the remote.
});

Dependencies

~1.3–2MB
~40K SLoC