carry transport proto end-to-end through OPEN/OPEN_ACK

OPEN and OPEN_ACK now carry proto:1 alongside upstream_id:1. The
session is tagged with its transport proto and reliability semantics
switch on the specific proto:

- TCP (proto=1): full L2 reliability (seq, ack, retransmit, in-order)
- UDP (proto=2, reserved): best-effort (no retransmit, no ordering,
  no pure ACKs — seq/ack_seq fields present but ignored)

This is architecturally correct: instead of a generic 'reliable:bool'
flag, each proto gets the semantics it needs. Today only TCP exists so
every session is reliable, but the extension point is clean for when
stateless protos are added.

Updated: PROTOCOL.md, Rust frame.rs/session.rs/main.rs,
C# Frame.cs/SessionManager.cs.
This commit is contained in:
2026-08-13 09:15:15 +00:00
parent eb8994d1e1
commit 7fa50dd4e4
6 changed files with 129 additions and 68 deletions
+9 -6
View File
@@ -147,10 +147,12 @@ async fn handle_frame(
};
let _ = tx.send((src, manifest.encode())).await;
}
Frame::Open { upstream_id } => {
let port = table.get(upstream_id).map(|u| u.port);
match port {
Some(port) => {
Frame::Open { upstream_id, proto } => {
let upstream = table.get(upstream_id);
match upstream {
Some(upstream) => {
let port = upstream.port;
let proto = upstream.proto.as_u8();
// Spawn so connect() doesn't block the rx loop.
let tx = tx.clone();
let store = Arc::clone(store);
@@ -165,15 +167,16 @@ async fn handle_frame(
sid,
SessionHandle {
upstream_id,
proto,
write: w,
send_state: Arc::new(Mutex::new(session::SendState::new())),
recv_state: Arc::new(Mutex::new(session::RecvState::new())),
peer_mac: src,
},
);
let ack = Frame::OpenAck { session_id: sid, upstream_id };
let ack = Frame::OpenAck { session_id: sid, upstream_id, proto };
let _ = tx.send((src, ack.encode())).await;
spawn_pump(r, sid, src, tx, store);
spawn_pump(r, sid, proto, src, tx, store);
}
Err(e) => {
error!("connect 127.0.0.1:{port} failed: {e}");