Skip to content

Commit 1a958c2

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 7945479 commit 1a958c2

File tree

2 files changed

+125
-13
lines changed

2 files changed

+125
-13
lines changed

dcsctp-cxx/examples/main.cpp

Lines changed: 89 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,104 @@
1313
// limitations under the License.
1414

1515
#include "dcsctp.h"
16+
17+
#include <cstdlib>
1618
#include <iostream>
19+
#include <stdexcept>
20+
#include <string>
21+
22+
// Polls for the next event and expects it to be a SendPacket.
23+
// Returns the packet payload. Throws a runtime_error if the event is not a
24+
// SendPacket.
25+
rust::Vec<uint8_t> expect_send_packet(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::SendPacket) {
29+
throw std::runtime_error("Expected SendPacket from " + socket_name +
30+
", but got something else.");
31+
}
32+
std::cout << "Polled SendPacket from " << socket_name
33+
<< " (size: " << event.packet.size() << ")" << std::endl;
34+
return event.packet;
35+
}
36+
37+
// Polls for the next event and expects it to be OnConnected.
38+
// Throws a runtime_error if the event is not OnConnected.
39+
void expect_on_connected(dcsctp_cxx::DcSctpSocket& socket,
40+
const std::string& socket_name) {
41+
dcsctp_cxx::Event event = dcsctp_cxx::poll_event(socket);
42+
if (event.event_type != dcsctp_cxx::EventType::OnConnected) {
43+
throw std::runtime_error("Expected OnConnected from " + socket_name +
44+
", but got something else.");
45+
}
46+
std::cout << "Polled OnConnected from " << socket_name << std::endl;
47+
}
48+
49+
// Polls for the next event and expects it to be Nothing.
50+
void expect_no_event(dcsctp_cxx::DcSctpSocket& socket,
51+
const std::string& socket_name) {
52+
dcsctp_cxx::Event event = dcsctp_cxx::poll_event(socket);
53+
if (event.event_type != dcsctp_cxx::EventType::Nothing) {
54+
throw std::runtime_error("Expected Nothing from " + socket_name +
55+
", but got something else.");
56+
}
57+
}
1758

1859
int main() {
1960
std::cout << "dcsctp version: " << dcsctp_cxx::version().c_str() << std::endl;
2061

21-
dcsctp_cxx::DcSctpSocket *socket = dcsctp_cxx::new_socket();
62+
dcsctp_cxx::DcSctpSocket* socket_a = dcsctp_cxx::new_socket();
63+
dcsctp_cxx::DcSctpSocket* socket_z = dcsctp_cxx::new_socket();
64+
std::cout << "Created two sockets: A and Z" << std::endl;
2265

23-
if (socket) {
24-
std::cout << "Successfully created a socket." << std::endl;
25-
} else {
26-
std::cout << "Failed to create a socket." << std::endl;
27-
return 1;
28-
}
66+
try {
67+
dcsctp_cxx::connect(*socket_a);
68+
69+
// A -> INIT -> Z
70+
rust::Vec<uint8_t> init_packet = expect_send_packet(*socket_a, "A");
71+
dcsctp_cxx::handle_input(*socket_z,
72+
{init_packet.data(), init_packet.size()});
73+
74+
// A <- INIT_ACK <- Z
75+
rust::Vec<uint8_t> init_ack_packet = expect_send_packet(*socket_z, "Z");
76+
dcsctp_cxx::handle_input(*socket_a, {init_ack_packet.data(),
77+
init_ack_packet.size()});
78+
79+
// A -> COOKIE_ECHO -> Z
80+
rust::Vec<uint8_t> cookie_echo_packet = expect_send_packet(*socket_a, "A");
81+
dcsctp_cxx::handle_input(
82+
*socket_z, {cookie_echo_packet.data(), cookie_echo_packet.size()});
83+
84+
// Z becomes connected
85+
expect_on_connected(*socket_z, "Z");
86+
87+
// A <- COOKIE_ACK <- Z
88+
rust::Vec<uint8_t> cookie_ack_packet = expect_send_packet(*socket_z, "Z");
89+
dcsctp_cxx::handle_input(
90+
*socket_a, {cookie_ack_packet.data(), cookie_ack_packet.size()});
91+
92+
// A becomes connected
93+
expect_on_connected(*socket_a, "A");
94+
95+
expect_no_event(*socket_a, "A");
96+
expect_no_event(*socket_z, "Z");
97+
98+
if (dcsctp_cxx::state(*socket_a) == dcsctp_cxx::SocketState::Connected &&
99+
dcsctp_cxx::state(*socket_z) == dcsctp_cxx::SocketState::Connected) {
100+
std::cout << "Both sockets connected successfully!" << std::endl;
101+
} else {
102+
std::cout << "Connection failed: sockets are not in Connected state."
103+
<< std::endl;
104+
return 1;
105+
}
29106

30-
if (dcsctp_cxx::state(*socket) == dcsctp_cxx::SocketState::Closed) {
31-
std::cout << "Socket is initially closed" << std::endl;
32-
} else {
33-
std::cout << "Socket is in an unexpected state" << std::endl;
107+
} catch (const std::runtime_error& e) {
108+
std::cerr << "Caught an exception: " << e.what() << std::endl;
34109
return 1;
35110
}
36111

37-
std::cout << "Socket is about to be deleted." << std::endl;
38-
dcsctp_cxx::delete_socket(socket);
112+
std::cout << "Sockets are about to be deleted." << std::endl;
113+
dcsctp_cxx::delete_socket(socket_a);
114+
dcsctp_cxx::delete_socket(socket_z);
39115
return 0;
40116
}

dcsctp-cxx/src/lib.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
use dcsctp::api::DcSctpSocket as DcSctpSocketTrait;
1616
use dcsctp::api::Options;
17+
use dcsctp::api::SocketEvent as DcSctpSocketEvent;
1718
use dcsctp::api::SocketState as DcSctpSocketState;
1819
use std::time::Instant;
1920

@@ -27,13 +28,28 @@ mod ffi {
2728
ShuttingDown,
2829
}
2930

31+
#[derive(Debug)]
32+
enum EventType {
33+
Nothing,
34+
OnConnected,
35+
SendPacket,
36+
}
37+
38+
struct Event {
39+
event_type: EventType,
40+
packet: Vec<u8>,
41+
}
42+
3043
extern "Rust" {
3144
type DcSctpSocket;
3245

3346
fn version() -> String;
3447
fn new_socket() -> *mut DcSctpSocket;
3548
unsafe fn delete_socket(socket: *mut DcSctpSocket);
3649
fn state(socket: &DcSctpSocket) -> SocketState;
50+
fn connect(socket: &mut DcSctpSocket);
51+
fn handle_input(socket: &mut DcSctpSocket, data: &[u8]);
52+
fn poll_event(socket: &mut DcSctpSocket) -> Event;
3753
}
3854
}
3955

@@ -66,3 +82,23 @@ fn state(socket: &DcSctpSocket) -> ffi::SocketState {
6682
DcSctpSocketState::ShuttingDown => ffi::SocketState::ShuttingDown,
6783
}
6884
}
85+
86+
fn connect(socket: &mut DcSctpSocket) {
87+
socket.0.connect();
88+
}
89+
90+
fn handle_input(socket: &mut DcSctpSocket, data: &[u8]) {
91+
socket.0.handle_input(data)
92+
}
93+
94+
fn poll_event(socket: &mut DcSctpSocket) -> ffi::Event {
95+
match socket.0.poll_event() {
96+
Some(DcSctpSocketEvent::SendPacket(p)) => {
97+
ffi::Event { event_type: ffi::EventType::SendPacket, packet: p }
98+
}
99+
Some(DcSctpSocketEvent::OnConnected()) => {
100+
ffi::Event { event_type: ffi::EventType::OnConnected, packet: Vec::new() }
101+
}
102+
_ => ffi::Event { event_type: ffi::EventType::Nothing, packet: Vec::new() },
103+
}
104+
}

0 commit comments

Comments
 (0)