Skip to main content
Virtual channels enable bidirectional communication between RDP client and server components beyond the standard graphics and input streams. IronRDP supports both Static Virtual Channels (SVCs) and Dynamic Virtual Channels (DVCs).

Channel Architecture

RDP supports up to 32 static channels per connection:
  • 1 mandatory I/O channel: Carries graphics, input, and control traffic
  • 31 optional static channels: Custom bidirectional data streams
Dynamic channels are created within a special static channel called DRDYNVC, allowing unlimited dynamic channels to be opened and closed during the session.

Static Virtual Channels (SVCs)

Static channels are established during connection setup and persist for the session lifetime.

Core Concepts

Channel Definition — SVCs are registered in the Client Network Data GCC block:
Each channel has:
  • 8-byte name: ASCII identifier (e.g., "CLIPRDR", "RDPSND", "RDPDR")
  • Options: Compression settings
  • Channel ID: Assigned by server during Basic Settings Exchange
Chunking — Large PDUs are automatically split into ~1600 byte chunks:
Each chunk includes a Channel PDU Header (CHANNEL_PDU_HEADER):
See ironrdp-svc/src/lib.rs:655-686.

Implementing an SVC

Implement the SvcProcessor trait:
See ironrdp-svc/src/lib.rs:258-295.

SvcMessage and Encoding

Response PDUs must implement SvcEncode:
Convert to SvcMessage:
See ironrdp-svc/src/lib.rs:68-121.

Registering Channels

Add channels to the connector before connection:
Channels are stored in a StaticChannelSet:
This enables retrieval by:
  • Type: get_by_type::<MyChannel>()
  • Channel ID: get_by_channel_id(channel_id)
  • Channel Name: get_by_channel_name(&name)
See ironrdp-svc/src/lib.rs:441-607.

Processing SVC Traffic

During the active session, route SVC data to the appropriate channel:
Chunking and MCS/X.224/TPKT framing is handled automatically by client_encode_svc_messages.

Compression

Specify compression behavior via CompressionCondition:
This sets the appropriate ChannelOptions flags in the Channel Definition:
  • COMPRESS_RDP: Compress if RDP data is compressed
  • COMPRESS: Always compress
See ironrdp-svc/src/lib.rs:123-132 and make_channel_options at line 426.

Example: Clipboard Channel

The clipboard channel (CLIPRDR) demonstrates a complete SVC implementation:
See crates/ironrdp-cliprdr for the full implementation.

Dynamic Virtual Channels (DVCs)

Dynamic channels are created at runtime over the DRDYNVC static channel.

DRDYNVC Protocol

The DRDYNVC channel manages dynamic channel lifecycle:
  1. Create Request: Client requests a new DVC by name
  2. Create Response: Server assigns a dynamic channel ID
  3. Data: Bidirectional data transfer using the channel ID
  4. Close: Either side closes the channel
PDU Types:
See ironrdp-dvc/src/pdu.rs.

Implementing a DVC

Implement the DvcProcessor trait:
See ironrdp-dvc/src/lib.rs:40-59.

DvcMessage and Encoding

DVC messages are trait objects:
Implement DvcEncode for your response types:
Return as DvcMessage:

Data Splitting

Large DVC messages are automatically split:
First chunk uses DataFirstPdu with total length:
See ironrdp-dvc/src/lib.rs:61-102.

Client-Side DVC Management

Use DvcClient to manage dynamic channels:
Opening a channel:
Sending data:
Closing a channel:
See ironrdp-dvc/src/client.rs.

Server-Side DVC Management

Use DvcServer for server-side channel management:
See ironrdp-dvc/src/server.rs.

Example: Display Control (EGFX)

The Enhanced Graphics Pipeline uses a DVC:
See:
  • crates/ironrdp-displaycontrol
  • crates/ironrdp-egfx

Encoding and Sending

SVC Encoding

Full encoding with MCS/X.224/TPKT headers:
This function:
  1. Chunkifies messages into ~1600 byte chunks
  2. Adds CHANNEL_PDU_HEADER to each chunk
  3. Wraps in MCS::SendDataRequest
  4. Wraps in X.224 Data TPDU
  5. Adds TPKT header
See ironrdp-svc/src/lib.rs:230-256.

DVC Encoding

Wrap DVC messages in DRDYNVC PDUs, then encode as SVC:
See ironrdp-dvc/src/lib.rs:61-102.

Channel Lifecycle

SVC Lifecycle

  1. Registration: Call with_static_channel() before connection
  2. Negotiation: Channel included in Client Network Data GCC block
  3. Assignment: Server assigns channel ID in Server Network Data
  4. Joining: Client sends Channel Join Request (unless skipped)
  5. Active: start() called, bidirectional communication enabled
  6. Termination: Channel closes when session ends

DVC Lifecycle

  1. Registration: Register DVC processor with DvcClient
  2. Creation: Client sends Create Request over DRDYNVC
  3. Assignment: Server responds with Create Response containing channel ID
  4. Active: start() called, bidirectional communication enabled
  5. Closure: Either side sends Close PDU
  6. Cleanup: close() called, resources released

Built-in Channels

IronRDP provides implementations for common channels:

Best Practices

Do:
  • Implement proper error handling in process() methods
  • Return empty Vec when no responses are needed
  • Use meaningful channel names (8 bytes for SVC, descriptive for DVC)
  • Leverage automatic chunkification — don’t manually split PDUs
  • Downcast channels safely using channel_processor_downcast_ref()
Don’t:
  • Block in process() or start() methods (no I/O)
  • Return None from start() unless explicitly intentional
  • Assume chunk size — use constants (CHANNEL_CHUNK_LENGTH, MAX_DATA_SIZE)
  • Forget to implement AsAny (use impl_as_any! macro)
  • Mix SVC and DVC encoding — they have different lifecycles

Debugging Channels

Enable tracing to observe channel traffic:
IronRDP uses structured logging throughout. See STYLE.md for logging conventions.

References

  • SVC Implementation: crates/ironrdp-svc/src/lib.rs
  • DVC Implementation: crates/ironrdp-dvc/src/lib.rs
  • Clipboard Example: crates/ironrdp-cliprdr
  • Device Redirection: crates/ironrdp-rdpdr
  • MS-RDPBCGR § 2.2.6: Virtual Channel PDU structures
  • MS-RDPEDYC: Dynamic Virtual Channel Extension specification