# How to Open a Bidirectional Stream on an Iroh Connection

> Learn how to open a bidirectional stream on an Iroh connection using Connection::open_bi. This enables simultaneous two-way data transfer for efficient communication.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: how-to-guide
- Published: 2026-06-18

---

**To open a bidirectional stream on an Iroh connection, call `Connection::open_bi` and await the resulting future, which resolves to a tuple containing a `SendStream` and `RecvStream` enabling simultaneous two-way data transfer.**

Iroh provides a QUIC-based networking stack that enables fast, reliable peer-to-peer communication. Opening a bidirectional stream is the standard way to exchange data bidirectionally over a single Iroh connection. This guide explains how to use the `open_bi` method in the `n0-computer/iroh` repository to establish concurrent read-write streams.

## Understanding Bidirectional Streams in Iroh

### What is a Bidirectional Stream?

A **bidirectional stream** is a pair of linked stream objects that allow data to flow in both directions over the same stream identifier. According to the Iroh source code, this consists of a `SendStream` for writing data and a `RecvStream` for reading incoming data from the peer.

### QUIC Foundation

Iroh’s networking layer is built on top of QUIC via the *quinn* crate. In [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs), the `Connection` struct abstracts the underlying QUIC implementation to provide a safe, async API for stream management.

## Using `Connection::open_bi`

The primary API for initiating bidirectional communication is the `open_bi` method defined on the `Connection` struct.

In [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs) at lines 822–834, the `open_bi` method is documented to return an `OpenBi<'_>` future. When awaited, this future resolves to a tuple `(SendStream, RecvStream)`. The `SendStream` handles outgoing data transmission, while the `RecvStream` manages incoming data from the peer.

### Critical Implementation Detail

A crucial behavior is documented at lines 845–848 of the same file: the initiating peer must write to the `SendStream` before the remote side can successfully complete `accept_bi`. If you await the `RecvStream` without writing anything first, the acceptance will block indefinitely.

## Complete Implementation Example

Here is a practical example demonstrating how to open a bidirectional stream, send data, and receive a response:

```rust
use iroh::endpoint::{Endpoint, Connection};
use tokio::io::{AsyncWriteExt, AsyncReadExt};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Build an endpoint (client or server) – see the `Endpoint` docs.
    let endpoint = Endpoint::client().await?;          // or `Endpoint::server(..)` for a listener
    // Connect to a remote peer – replace with the peer's address.
    let conn: Connection = endpoint.connect(remote_addr).await?;

    // ---------- Open a bidirectional stream ----------
    // `open_bi` returns a future that resolves to (send, recv).
    let (mut send, mut recv) = conn.open_bi().await?;

    // Write some data to the peer.
    send.write_all(b"hello iroh!").await?;
    // It's important to flush / close the send side if you expect the peer
    // to read the data before you start reading.
    send.finish().await?;

    // Read the peer’s response.
    let mut peer_buf = Vec::new();
    recv.read_to_end(&mut peer_buf).await?;
    println!("Received: {}", String::from_utf8_lossy(&peer_buf));

    Ok(())
}

```

This pattern appears in the repository’s example programs. The **echo** example in [`iroh/examples/echo.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/echo.rs) opens a bidirectional stream, writes a payload, and reads the echoed data back, providing a fully working demonstration of this API.

## Stream Limits and Configuration

The number of concurrent bidirectional streams is constrained by the QUIC transport configuration. Specifically, the limit is controlled by `QuicTransportConfig::max_concurrent_bidirectional_streams`. You can open multiple concurrent streams up to this configured limit, with each stream operating independently. The underlying stream definitions and QUIC-specific plumbing are located in [`iroh/src/endpoint/quic.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/quic.rs).

## Summary

- **Use `Connection::open_bi`**: Call this method on an active `Connection` to initiate a bidirectional stream as implemented in [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs).
- **Await the tuple**: The method returns a future that resolves to `(SendStream, RecvStream)` for full-duplex communication.
- **Write before read**: Always write data to the `SendStream` before awaiting the `RecvStream` to prevent blocking the remote peer's `accept_bi` call, as documented at lines 845–848.
- **Reference implementations**: Study [`iroh/examples/echo.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/echo.rs) for working patterns and [`iroh/src/endpoint/quic.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/quic.rs) for stream type definitions.
- **Respect limits**: Monitor `max_concurrent_bidirectional_streams` in your transport configuration to avoid exceeding connection limits.

## Frequently Asked Questions

### What is the difference between `open_bi` and `accept_bi`?

`open_bi` initiates a new bidirectional stream from the local peer to the remote peer, while `accept_bi` listens for incoming stream requests from the remote side. The initiator must write data to the `SendStream` before the receiver can successfully complete `accept_bi` and begin reading.

### Why does my bidirectional stream block when reading?

According to the Iroh source code at lines 845–848 of [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs), the receiving side of a bidirectional stream will block if the initiating peer has not written any data to its `SendStream`. Ensure you write to the stream before attempting to read from the corresponding `RecvStream`.

### How many bidirectional streams can I open on one connection?

The number is limited by the `QuicTransportConfig::max_concurrent_bidirectional_streams` setting. You can create any number of concurrent bidirectional streams up to this configured limit, subject to the underlying QUIC transport constraints.

### Where can I find working examples of bidirectional streams in Iroh?

The [`iroh/examples/echo.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/echo.rs) file in the n0-computer/iroh repository demonstrates a complete working implementation. It opens a bidirectional stream, transmits data, and processes the response, serving as the canonical reference for stream usage patterns.