#web-rtc #p2p #networking #peer #web-native #peer-connection #load-balancing

just-webrtc-signalling

Just simple, fast and easy signalling for full-mesh WebRTC connections on native and wasm

5 releases

0.2.1 Sep 30, 2024
0.2.0 Apr 5, 2024
0.1.2 Mar 30, 2024
0.1.1 Mar 29, 2024
0.1.0 Mar 29, 2024

#2233 in Network programming

MIT/Apache

64KB
1K SLoC

Just WebRTC Signalling

crates.io documentation license build status

Just simple, fast and easy signalling for full-mesh WebRTC connections in Rust.

Provides a TLS secure-able server and a client that is both native and wasm32 compatible.

This signalling implementation is built around a tonic gRPC service. gRPC is leveraged for its efficiency, security, scalability and load balancing. In the future, interoperability may also be used to signal between clients written in different languages.

just-webrtc-signalling is modular. Use it with just-webrtc... or with any other WebRTC implementation you like!

[dependencies]
just-webrtc-signalling = "0.2"

Documentation

See docs.rs for the complete API reference.

Feature flags

All features [client, server and server_web] are enabled by default.

  • client: enables the signalling client (native and wasm compatible)
  • server: enables the native signalling service (for communication with native clients)
  • server-web: enables the web signalling service (for communication with web/wasm clients)

Examples

Running a client:

The below example demonstrates how you might integrate a just-webrtc-signalling client with the WebRTC implementation of your choice.

NOTE: for web clients, you will need to disable the server and server-web features, as these are native only:

[dependencies]
just-webrtc-signalling = { version = "0.1", default-features = false, features = ["client"] }

For complete examples, using just-webrtc as the WebRTC implementation, see the Examples in the repository.

use anyhow::Result;
use futures_util::FutureExt;

use just_webrtc_signalling::client::RtcSignallingClientBuilder;

/// run all client signalling concurrently
async fn run_peer(server_address: String) -> Result<()> {
    // create signalling client
    let signalling_client = RtcSignallingClientBuilder::default().build(server_address)?;
    // start a peer
    let mut signalling_peer = signalling_client.start_peer().await?;
    // set callbacks for the new peer
    signalling_peer.set_on_create_offer(create_offer);
    signalling_peer.set_on_receive_answer(receive_answer);
    signalling_peer.set_on_local_sig_cplt(local_sig_cplt);
    signalling_peer.set_on_receive_offer(receive_offer);
    signalling_peer.set_on_remote_sig_cplt(remote_sig_cplt);
    // run the signalling peer
    loop {
        signalling_peer.step().await?;
    }
    Ok(())
}

// For the purpose of this example, the descriptions and candidates will be strings.
// In actual code, these types would be serializable types provided by the WebRTC implementation
type Description = String;
type ICECandidates = String;

/// implement create offer callback (called by the signalling client to create an offer)
async fn create_offer(remote_id: u64) -> Result<(u64, (Description, ICECandidates))> {
    // User's WebRTC implementation creates a local peer connection here.
    // Generating an offer and ICE candidates and returning them.
    // The signalling client will deliver the signal to the remote peer with the corresponding `remote_id`
    let offer = Description::new();
    let offerer_candidates = ICECandidates::new();
    Ok((remote_id, (offer, offerer_candidates)))
}

/// implement receive answer callback (called by the signalling client to deliver an answer)
async fn receive_answer(remote_id: u64, answer_set: (Description, ICECandidates)) -> Result<u64> {
    // User's WebRTC implementation receives an answer from a remote peer to a local peer connection here.
    Ok(remote_id)
}

/// implement 'local' (offerer peer) signalling complete callback
async fn local_sig_cplt(remote_id: u64) -> Result<u64> {
    // User's WebRTC implementation may now open/receive data channels
    // and perform send/receives across channels.
    Ok(remote_id)
}

/// implement receive offer callback (called by the signalling client to deliver an offer and create an answer)
async fn receive_offer(remote_id: u64, offer_set: (Description, ICECandidates)) -> Result<(u64, (Description, ICECandidates))> {
    // User's WebRTC implementation creates a remote peer connection from the remote offer and candidates here.
    // Generating an answer and ICE candidates and returning them.
    // The signalling client will deliver the signal to the remote peer with the corresponding `remote_id`
    let answer = Description::new();
    let answerer_candidates = ICECandidates::new();
    Ok((remote_id, (answer, answerer_candidates)))
}

/// implement 'remote' (answerer peer) signalling complete callback
async fn remote_sig_cplt(remote_id: u64) -> Result<u64> {
    // User's WebRTC implementation may now open/receive data channels
    // and perform send/receives across channels.
    Ok(remote_id)
}

Running a server:

Running a just-webrtc-signalling server is simple. Just configure & create the services you need and run concurrently!

You can also add RtcSignallingService alongside other services on your existing tonic servers!

The below example runs two services, one for native clients and one for web clients. The services share common signalling channels to enable cross-platform signalling between web and native.

use std::sync::Arc;

use anyhow::Result;
use just_webrtc_signalling::{server::Signalling, DEFAULT_NATIVE_SERVER_ADDR, DEFAULT_WEB_SERVER_ADDR};

async fn serve() -> Result<()> {
    // create shared mapped signalling channels
    let signalling = Arc::new(Signalling::new());
    // create service futures
    let serve_fut = just_webrtc_signalling::server::serve(
        signalling.clone(),
        DEFAULT_NATIVE_SERVER_ADDR.parse()?,
        None, None,  // no keepalive interval or timeout
        None,  // no TLS
    );
    let serve_web_fut = just_webrtc_signalling::server::serve_web(
        signalling,
        DEFAULT_WEB_SERVER_ADDR.parse()?,
        None, None,  // no keepalive interval or timeout
        None,  // no TLS
    );
    // run native and web facing services concurrently
    tokio::try_join!(serve_fut, serve_web_fut)?;
    Ok(())
}

License

This project is licensed under either of

at your option.

Dependencies

~0.7–14MB
~184K SLoC