Skip to content

Commit b1b2c1a

Browse files
committed
Expose connection logic to C++ FFI
This change exposes the basic functionality required to establish a dcSCTP connection from a C++ application. It adds FFI bindings for `connect`, `handle_input`, and `poll_event`. To model this in C++, `poll_event` now returns an `Event` struct, which can be used to distinguish between different socket events such as `OnConnected` and `SendPacket`. As the cxx FFI doesn't support rich types such as std::variant, having a fat struct as interface between Rust and C++ is simple and doesn't add much overhead. The C++ example in `dcsctp-cxx/examples/main.cpp` has been updated to demonstrate the explicit connection handshake between two sockets, mirroring the logic in the `establish_connection` Rust test.
1 parent 907e7ce commit b1b2c1a

File tree

2 files changed

+130
-13
lines changed

2 files changed

+130
-13
lines changed

dcsctp-cxx/examples/main.cpp

Lines changed: 89 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,102 @@
11
#include "dcsctp.h"
2+
#include <cstdlib>
23
#include <iostream>
4+
#include <stdexcept>
5+
#include <string>
6+
#include <vector>
7+
8+
// Polls for the next event and expects it to be a SendPacket.
9+
// Returns the packet payload. Throws a runtime_error if the event is not a
10+
// SendPacket.
11+
rust::Vec<uint8_t> expect_send_packet(dcsctp_cxx::DcSctpSocket& socket,
12+
const std::string& socket_name) {
13+
dcsctp_cxx::Event event = dcsctp_cxx::poll_event(socket);
14+
if (event.event_type != dcsctp_cxx::EventType::SendPacket) {
15+
throw std::runtime_error("Expected SendPacket from " + socket_name +
16+
", but got something else.");
17+
}
18+
std::cout << "Polled SendPacket from " << socket_name
19+
<< " (size: " << event.packet.size() << ")" << std::endl;
20+
return event.packet;
21+
}
22+
23+
// Polls for the next event and expects it to be OnConnected.
24+
// Throws a runtime_error if the event is not OnConnected.
25+
void expect_on_connected(dcsctp_cxx::DcSctpSocket& socket,
26+
const std::string& socket_name) {
27+
dcsctp_cxx::Event event = dcsctp_cxx::poll_event(socket);
28+
if (event.event_type != dcsctp_cxx::EventType::OnConnected) {
29+
throw std::runtime_error("Expected OnConnected from " + socket_name +
30+
", but got something else.");
31+
}
32+
std::cout << "Polled OnConnected from " << socket_name << std::endl;
33+
}
34+
35+
// Polls for the next event and expects it to be Nothing.
36+
void expect_no_event(dcsctp_cxx::DcSctpSocket& socket,
37+
const std::string& socket_name) {
38+
dcsctp_cxx::Event event = dcsctp_cxx::poll_event(socket);
39+
if (event.event_type != dcsctp_cxx::EventType::Nothing) {
40+
throw std::runtime_error("Expected Nothing from " + socket_name +
41+
", but got something else.");
42+
}
43+
}
344

445
int main() {
546
std::cout << "dcsctp version: " << dcsctp_cxx::version().c_str() << std::endl;
647

7-
dcsctp_cxx::DcSctpSocket *socket = dcsctp_cxx::new_socket();
48+
dcsctp_cxx::DcSctpSocket* socket_a = dcsctp_cxx::new_socket();
49+
dcsctp_cxx::DcSctpSocket* socket_z = dcsctp_cxx::new_socket();
50+
std::cout << "Created two sockets: A and Z" << std::endl;
851

9-
if (socket) {
10-
std::cout << "Successfully created a socket." << std::endl;
11-
} else {
12-
std::cout << "Failed to create a socket." << std::endl;
13-
return 1;
14-
}
52+
try {
53+
dcsctp_cxx::connect(*socket_a);
54+
55+
// A -> INIT -> Z
56+
rust::Vec<uint8_t> init_packet = expect_send_packet(*socket_a, "A");
57+
dcsctp_cxx::handle_input(*socket_z,
58+
{init_packet.data(), init_packet.size()});
59+
60+
// A <- INIT_ACK <- Z
61+
rust::Vec<uint8_t> init_ack_packet = expect_send_packet(*socket_z, "Z");
62+
dcsctp_cxx::handle_input(*socket_a, {init_ack_packet.data(),
63+
init_ack_packet.size()});
64+
65+
// A -> COOKIE_ECHO -> Z
66+
rust::Vec<uint8_t> cookie_echo_packet = expect_send_packet(*socket_a, "A");
67+
dcsctp_cxx::handle_input(
68+
*socket_z, {cookie_echo_packet.data(), cookie_echo_packet.size()});
69+
70+
// Z becomes connected
71+
expect_on_connected(*socket_z, "Z");
72+
73+
// A <- COOKIE_ACK <- Z
74+
rust::Vec<uint8_t> cookie_ack_packet = expect_send_packet(*socket_z, "Z");
75+
dcsctp_cxx::handle_input(
76+
*socket_a, {cookie_ack_packet.data(), cookie_ack_packet.size()});
77+
78+
// A becomes connected
79+
expect_on_connected(*socket_a, "A");
80+
81+
expect_no_event(*socket_a, "A");
82+
expect_no_event(*socket_z, "Z");
83+
84+
if (dcsctp_cxx::state(*socket_a) == dcsctp_cxx::SocketState::Connected &&
85+
dcsctp_cxx::state(*socket_z) == dcsctp_cxx::SocketState::Connected) {
86+
std::cout << "Both sockets connected successfully!" << std::endl;
87+
} else {
88+
std::cout << "Connection failed: sockets are not in Connected state."
89+
<< std::endl;
90+
return 1;
91+
}
1592

16-
if (dcsctp_cxx::state(*socket) == dcsctp_cxx::SocketState::Closed) {
17-
std::cout << "Socket is initially closed" << std::endl;
18-
} else {
19-
std::cout << "Socket is in an unexpected state" << std::endl;
93+
} catch (const std::runtime_error& e) {
94+
std::cerr << "Caught an exception: " << e.what() << std::endl;
2095
return 1;
2196
}
2297

23-
std::cout << "Socket is about to be deleted." << std::endl;
24-
dcsctp_cxx::delete_socket(socket);
98+
std::cout << "Sockets are about to be deleted." << std::endl;
99+
dcsctp_cxx::delete_socket(socket_a);
100+
dcsctp_cxx::delete_socket(socket_z);
25101
return 0;
26102
}

dcsctp-cxx/src/lib.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use dcsctp::api::DcSctpSocket as DcSctpSocketTrait;
22
use dcsctp::api::Options;
3+
use dcsctp::api::SocketEvent as DcSctpSocketEvent;
34
use dcsctp::api::SocketState as DcSctpSocketState;
45
use std::time::Instant;
56

@@ -13,13 +14,28 @@ mod ffi {
1314
ShuttingDown,
1415
}
1516

17+
#[derive(Debug)]
18+
enum EventType {
19+
Nothing,
20+
OnConnected,
21+
SendPacket,
22+
}
23+
24+
struct Event {
25+
event_type: EventType,
26+
packet: Vec<u8>,
27+
}
28+
1629
extern "Rust" {
1730
type DcSctpSocket;
1831

1932
fn version() -> String;
2033
fn new_socket() -> *mut DcSctpSocket;
2134
unsafe fn delete_socket(socket: *mut DcSctpSocket);
2235
fn state(socket: &DcSctpSocket) -> SocketState;
36+
fn connect(socket: &mut DcSctpSocket);
37+
fn handle_input(socket: &mut DcSctpSocket, data: &[u8]);
38+
fn poll_event(socket: &mut DcSctpSocket) -> Event;
2339
}
2440
}
2541

@@ -52,3 +68,28 @@ fn state(socket: &DcSctpSocket) -> ffi::SocketState {
5268
DcSctpSocketState::ShuttingDown => ffi::SocketState::ShuttingDown,
5369
}
5470
}
71+
72+
fn connect(socket: &mut DcSctpSocket) {
73+
socket.0.connect();
74+
}
75+
76+
fn handle_input(socket: &mut DcSctpSocket, data: &[u8]) {
77+
socket.0.handle_input(data)
78+
}
79+
80+
fn poll_event(socket: &mut DcSctpSocket) -> ffi::Event {
81+
match socket.0.poll_event() {
82+
Some(DcSctpSocketEvent::SendPacket(p)) => ffi::Event {
83+
event_type: ffi::EventType::SendPacket,
84+
packet: p,
85+
},
86+
Some(DcSctpSocketEvent::OnConnected()) => ffi::Event {
87+
event_type: ffi::EventType::OnConnected,
88+
packet: Vec::new(),
89+
},
90+
_ => ffi::Event {
91+
event_type: ffi::EventType::Nothing,
92+
packet: Vec::new(),
93+
},
94+
}
95+
}

0 commit comments

Comments
 (0)