Client Architecture
An IronRDP client consists of three main phases:- Connection - Establish TCP connection and negotiate RDP protocol parameters
- Authentication - Perform TLS upgrade and CredSSP authentication
- Active Session - Process incoming PDUs and handle graphics/input
Blocking Client Example
Here’s how to build a basic blocking client that captures a screenshot (based onexamples/screenshot.rs):
use ironrdp::connector::{self, Credentials, DesktopSize};
use ironrdp::pdu::gcc::KeyboardType;
use ironrdp::pdu::rdp::capability_sets::MajorPlatformType;
use ironrdp_pdu::rdp::client_info::PerformanceFlags;
let config = connector::Config {
credentials: Credentials::UsernamePassword {
username: "user".to_owned(),
password: "pass".to_owned(),
},
domain: None,
enable_tls: false,
enable_credssp: true,
keyboard_type: KeyboardType::IbmEnhanced,
keyboard_subtype: 0,
keyboard_layout: 0,
keyboard_functional_keys_count: 12,
desktop_size: DesktopSize {
width: 1280,
height: 1024,
},
client_name: "my-rdp-client".to_owned(),
client_build: 0,
platform: MajorPlatformType::UNIX,
performance_flags: PerformanceFlags::default(),
// ... additional fields
};
use std::net::TcpStream;
use ironrdp_blocking::Framed;
// Connect TCP
let tcp_stream = TcpStream::connect(("server.example.com", 3389))?;
let client_addr = tcp_stream.local_addr()?;
let mut framed = Framed::new(tcp_stream);
let mut connector = connector::ClientConnector::new(config, client_addr);
// Begin connection sequence
let should_upgrade = ironrdp_blocking::connect_begin(&mut framed, &mut connector)?;
use tokio_rustls::rustls;
// Get raw stream before TLS upgrade
let initial_stream = framed.into_inner_no_leftover();
// Configure TLS (simplified - see full example for certificate verification)
let mut tls_config = rustls::client::ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(/* ... */)
.with_no_client_auth();
tls_config.resumption = rustls::client::Resumption::disabled();
let server_name = "server.example.com".try_into()?;
let client = rustls::ClientConnection::new(Arc::new(tls_config), server_name)?;
let mut tls_stream = rustls::StreamOwned::new(client, initial_stream);
tls_stream.flush()?;
// Extract server public key for CredSSP
let cert = tls_stream.conn.peer_certificates()
.and_then(|certs| certs.first())
.context("peer certificate is missing")?;
let server_public_key = extract_server_public_key(cert)?;
// Mark as upgraded and create new framed wrapper
let upgraded = ironrdp_blocking::mark_as_upgraded(should_upgrade, &mut connector);
let mut upgraded_framed = Framed::new(tls_stream);
use sspi::network_client::reqwest_network_client::ReqwestNetworkClient;
let mut network_client = ReqwestNetworkClient;
let connection_result = ironrdp_blocking::connect_finalize(
upgraded,
connector,
&mut upgraded_framed,
&mut network_client,
"server.example.com".into(),
server_public_key,
None, // kerberos_config
)?;
use ironrdp::session::{ActiveStage, ActiveStageOutput};
use ironrdp::session::image::DecodedImage;
let mut image = DecodedImage::new(
ironrdp_graphics::image_processing::PixelFormat::RgbA32,
connection_result.desktop_size.width,
connection_result.desktop_size.height,
);
let mut active_stage = ActiveStage::new(connection_result);
loop {
let (action, payload) = match framed.read_pdu() {
Ok(result) => result,
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => break,
Err(e) => return Err(e.into()),
};
let outputs = active_stage.process(&mut image, action, &payload)?;
for out in outputs {
match out {
ActiveStageOutput::ResponseFrame(frame) => {
framed.write_all(&frame)?;
}
ActiveStageOutput::Terminate(_) => break,
_ => {}
}
}
}
Async Client with Tokio
For asynchronous clients, useironrdp-async and ironrdp-tokio:
Full-Featured Client
For a production-ready client with:- Window management (using
winit) - Software rendering (using
softbuffer) - Input handling
- Clipboard support
ironrdp-client crate source code.
Key Configuration Options
Performance Flags
Optimize for different scenarios:Compression
Enable graphics compression:Desktop Size
Specify custom resolution:Error Handling
IronRDP usesironrdp::connector::ConnectorError for connection errors and session-specific errors during the active stage:
Next Steps
- Virtual Channels - Add clipboard, audio, and custom channels
- Graphics Rendering - Optimize graphics decoding
- Async I/O - Build high-performance async clients

