Skip to content

Commit f637332

Browse files
committed
Properly multiplex exchanges and sessions
1 parent 014ae24 commit f637332

File tree

7 files changed

+111
-95
lines changed

7 files changed

+111
-95
lines changed

MatterDotNet/Protocol/Connection/IConnection.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ namespace MatterDotNet.Protocol.Connection
1717
{
1818
internal interface IConnection : IDisposable
1919
{
20-
Task<Frame> Read();
2120
Task SendFrame(Exchange exchange, Frame frame, bool reliable);
2221
Task CloseExchange(Exchange exchange);
2322
}

MatterDotNet/Protocol/Connection/MRPConnection.cs

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ internal class MRPConnection : IConnection
3232

3333
ConcurrentDictionary<(ushort, ushort), Retransmission> Retransmissions = new ConcurrentDictionary<(ushort, ushort), Retransmission>();
3434
ConcurrentDictionary<ushort, uint> AckTable = new ConcurrentDictionary<ushort, uint>();
35-
Channel<Frame> channel = Channel.CreateUnbounded<Frame>();
3635
CancellationTokenSource cts = new CancellationTokenSource();
3736

3837
UdpClient client;
@@ -70,7 +69,7 @@ public async Task SendFrame(Exchange exchange, Frame frame, bool reliable)
7069
}
7170
}
7271
}
73-
Console.WriteLine("SENT: " + frame.ToString());
72+
Console.WriteLine(DateTime.Now.ToString("h:mm:ss") + " SENT: " + frame.ToString());
7473
await client.SendAsync(writer.GetPayload());
7574
exchange.Session.Timestamp = DateTime.Now;
7675
while (reliable)
@@ -111,22 +110,22 @@ public async Task SendAck(SessionContext? session, ushort exchange, uint counter
111110
ack.Message.ExchangeID = exchange;
112111
ack.Message.Flags = ExchangeFlags.Acknowledgement;
113112
if (initiator)
113+
{
114+
ack.Flags |= MessageFlags.SourceNodeID;
114115
ack.Message.Flags |= ExchangeFlags.Initiator;
116+
}
117+
else
118+
ack.Flags |= MessageFlags.DestinationNodeID;
115119
ack.Message.AckCounter = counter;
116120
ack.Message.Protocol = Payloads.ProtocolType.SecureChannel;
117121
PayloadWriter writer = new PayloadWriter(Frame.MAX_SIZE + 4);
118122
ack.Serialize(writer, session!);
119123
if (AckTable.TryGetValue(exchange, out uint ctr) && ctr == counter)
120124
AckTable.TryRemove(exchange, out _);
121-
Console.WriteLine("Sent standalone ack: " + ack.ToString());
125+
Console.WriteLine(DateTime.Now.ToString("h:mm:ss") + " Sent standalone ack: " + ack.ToString());
122126
await client.SendAsync(writer.GetPayload());
123127
}
124128

125-
public async Task<Frame> Read()
126-
{
127-
return await channel.Reader.ReadAsync();
128-
}
129-
130129
public async Task Run()
131130
{
132131
try
@@ -135,10 +134,20 @@ public async Task Run()
135134
{
136135
UdpReceiveResult result = await client.ReceiveAsync();
137136
Frame frame = new Frame(result.Buffer);
137+
if (!frame.Valid)
138+
{
139+
Console.WriteLine("Invalid frame received");
140+
continue;
141+
}
142+
SessionContext? session = SessionManager.GetSession(frame.SessionID);
143+
bool ack = false;
138144
if ((frame.Message.Flags & ExchangeFlags.Reliability) == ExchangeFlags.Reliability)
139145
{
140146
if (!AckTable.TryAdd(frame.Message.ExchangeID, frame.Counter))
141-
await SendAck(SessionManager.GetSession(frame.SessionID), frame.Message.ExchangeID, frame.Counter, (frame.Message.Flags & ExchangeFlags.Initiator) == 0);
147+
{
148+
ack = true;
149+
await SendAck(session, frame.Message.ExchangeID, frame.Counter, (frame.Message.Flags & ExchangeFlags.Initiator) == 0);
150+
}
142151
}
143152
if ((frame.Message.Flags & ExchangeFlags.Acknowledgement) == ExchangeFlags.Acknowledgement)
144153
{
@@ -148,20 +157,23 @@ public async Task Run()
148157
transmission.Ack.Release();
149158
}
150159
}
151-
if (frame.SessionID != 0)
152-
SessionManager.SessionActive(frame.SessionID);
153-
Console.WriteLine("Received: " + frame.ToString());
154-
channel.Writer.TryWrite(frame);
160+
Console.WriteLine(DateTime.Now.ToString("h:mm:ss") + " Received: " + frame.ToString());
161+
if (session == null)
162+
{
163+
Console.WriteLine("Unknown Session: " + frame.SessionID);
164+
continue;
165+
}
166+
if (!session.ProcessFrame(frame) && !ack)
167+
await SendAck(session, frame.Message.ExchangeID, frame.Counter, (frame.Message.Flags & ExchangeFlags.Initiator) == 0);
168+
169+
session.Timestamp = DateTime.Now;
170+
session.LastActive = DateTime.Now;
155171
}
156172
}
157173
catch (Exception e)
158174
{
159175
Console.WriteLine(e.ToString());
160176
}
161-
finally
162-
{
163-
channel.Writer.Complete();
164-
}
165177
}
166178

167179
public async Task CloseExchange(Exchange exchange)

MatterDotNet/Protocol/Connection/TCPConnection.cs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ internal class TCPConnection : IConnection
2424
TcpClient client;
2525
NetworkStream stream;
2626
CancellationTokenSource cts = new CancellationTokenSource();
27-
Channel<Frame> channel = Channel.CreateUnbounded<Frame>();
2827
public TCPConnection(IPEndPoint destination)
2928
{
3029
client = new TcpClient();
@@ -43,11 +42,6 @@ public async Task SendFrame(Exchange exchange, Frame frame, bool reliable)
4342
exchange.Session.Timestamp = DateTime.Now;
4443
}
4544

46-
public async Task<Frame> Read()
47-
{
48-
return await channel.Reader.ReadAsync();
49-
}
50-
5145
public async Task Run()
5246
{
5347
byte[] len = new byte[4];
@@ -58,13 +52,18 @@ public async Task Run()
5852
await stream.ReadExactlyAsync(len);
5953
frameLen = BinaryPrimitives.ReadInt32LittleEndian(len);
6054
await stream.ReadExactlyAsync(data.Slice(0, frameLen));
61-
Console.WriteLine("READ: " + Convert.ToHexString(data.Slice(0, frameLen).Span));
6255
Frame frame = new Frame(data.Slice(0, frameLen).Span);
63-
channel.Writer.TryWrite(frame);
64-
if (frame.SessionID != 0)
65-
SessionManager.SessionActive(frame.SessionID);
56+
Console.WriteLine(DateTime.Now.ToString("h:mm:ss") + " Received: " + frame.ToString());
57+
SessionContext? session = SessionManager.GetSession(frame.SessionID);
58+
if (session == null)
59+
{
60+
Console.WriteLine("Unknown Session: " + frame.SessionID);
61+
continue;
62+
}
63+
session.ProcessFrame(frame);
64+
session.Timestamp = DateTime.Now;
65+
session.LastActive = DateTime.Now;
6666
}
67-
channel.Writer.Complete();
6867
}
6968

7069
public void Dispose()

MatterDotNet/Protocol/Sessions/Exchange.cs

Lines changed: 5 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,21 @@
1212

1313
using MatterDotNet.Protocol.Payloads;
1414
using MatterDotNet.Protocol.Payloads.Flags;
15+
using System.Threading.Channels;
1516

1617
namespace MatterDotNet.Protocol.Sessions
1718
{
1819
internal class Exchange : IDisposable
1920
{
20-
private const int MSG_COUNTER_WINDOW_SIZE = 32;
21-
2221
public ushort ID { get; init; }
2322
public SessionContext Session {get; init;}
23+
internal Channel<Frame> Messages { get; init;}
2424

2525
internal Exchange(SessionContext session, ushort id)
2626
{
2727
Session = session;
2828
ID = id;
29+
Messages = Channel.CreateBounded<Frame>(10);
2930
}
3031

3132
public async Task SendFrame(Frame frame, bool reliable = true)
@@ -38,62 +39,9 @@ public async Task SendFrame(Frame frame, bool reliable = true)
3839
await Session.Connection.SendFrame(this, frame, reliable);
3940
}
4041

41-
public async Task<Frame> Read()
42+
public async Task<Frame> Read(CancellationToken token = default)
4243
{
43-
Frame? frame = null;
44-
while (frame == null)
45-
{
46-
frame = await Session.Connection.Read();
47-
MessageState state = Session.PeerMessageCtr;
48-
if (!state.Initialized)
49-
{
50-
state.Initialized = true;
51-
state.CounterWindow = uint.MaxValue;
52-
state.MaxMessageCounter = frame.Counter;
53-
}
54-
else if (frame.Counter > state.MaxMessageCounter)
55-
{
56-
int offset = (int)Math.Min(frame.Counter - state.MaxMessageCounter, MSG_COUNTER_WINDOW_SIZE);
57-
state.MaxMessageCounter = frame.Counter;
58-
state.CounterWindow <<= offset;
59-
if (offset < MSG_COUNTER_WINDOW_SIZE)
60-
state.CounterWindow |= (uint)(1 << (int)offset - 1);
61-
}
62-
else if (frame.Counter == state.MaxMessageCounter)
63-
{
64-
Console.WriteLine("DROPPED DUPLICATE: " + frame);
65-
frame = null;
66-
}
67-
else
68-
{
69-
uint offset = (state.MaxMessageCounter - frame.Counter);
70-
if (offset > MSG_COUNTER_WINDOW_SIZE)
71-
{
72-
if (Session is SecureSession)
73-
{
74-
Console.WriteLine("DROPPED DUPLICATE <behind window>: " + frame);
75-
frame = null;
76-
}
77-
else
78-
{
79-
state.MaxMessageCounter = frame.Counter;
80-
state.CounterWindow = uint.MaxValue;
81-
}
82-
}
83-
else
84-
{
85-
if ((state.CounterWindow & (uint)(1 << (int)offset - 1)) != 0x0)
86-
{
87-
Console.WriteLine("DROPPED DUPLICATE <within window>: " + frame);
88-
frame = null;
89-
}
90-
else
91-
state.CounterWindow |= (uint)(1 << (int)offset - 1);
92-
}
93-
}
94-
Session.PeerMessageCtr = state;
95-
}
96-
return frame;
44+
return await Messages.Reader.ReadAsync(token);
9745
}
9846

9947
/// <inheritdoc />

MatterDotNet/Protocol/Sessions/SecureSession.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
using MatterDotNet.Protocol.Connection;
1414
using MatterDotNet.Protocol.Cryptography;
15+
using MatterDotNet.Protocol.Payloads;
1516
using System.Buffers.Binary;
1617
using System.Security.Cryptography;
1718

@@ -49,6 +50,12 @@ internal SecureSession(IConnection connection, bool PASE, bool initiator, ushort
4950
}
5051
}
5152

53+
internal override bool HandleBehindWindow(ref MessageState state, Frame frame)
54+
{
55+
Console.WriteLine("DROPPED DUPLICATE <behind window>: " + frame);
56+
return true;
57+
}
58+
5259
internal override uint GetSessionCounter()
5360
{
5461
return LocalMessageCtr;

MatterDotNet/Protocol/Sessions/SessionContext.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ namespace MatterDotNet.Protocol.Sessions
2020
{
2121
public class SessionContext : IDisposable
2222
{
23+
private const int MSG_COUNTER_WINDOW_SIZE = 32;
24+
2325
public bool Initiator { get; init; }
2426
public ulong InitiatorNodeID { get; init; }
2527
public ushort LocalSessionID { get; init; }
@@ -66,6 +68,64 @@ internal Exchange CreateExchange()
6668
return ret;
6769
}
6870

71+
internal bool ProcessFrame(Frame frame)
72+
{
73+
MessageState state = PeerMessageCtr;
74+
if (!state.Initialized)
75+
{
76+
state.Initialized = true;
77+
state.CounterWindow = uint.MaxValue;
78+
state.MaxMessageCounter = frame.Counter;
79+
}
80+
else if (frame.Counter > state.MaxMessageCounter)
81+
{
82+
int offset = (int)Math.Min(frame.Counter - state.MaxMessageCounter, MSG_COUNTER_WINDOW_SIZE);
83+
state.MaxMessageCounter = frame.Counter;
84+
state.CounterWindow <<= offset;
85+
if (offset < MSG_COUNTER_WINDOW_SIZE)
86+
state.CounterWindow |= (uint)(1 << (int)offset - 1);
87+
}
88+
else if (frame.Counter == state.MaxMessageCounter)
89+
{
90+
Console.WriteLine("DROPPED DUPLICATE <repeated last>: " + frame);
91+
return false;
92+
}
93+
else
94+
{
95+
uint offset = (state.MaxMessageCounter - frame.Counter);
96+
if (offset > MSG_COUNTER_WINDOW_SIZE)
97+
{
98+
if (HandleBehindWindow(ref state, frame))
99+
return false;
100+
}
101+
else
102+
{
103+
if ((state.CounterWindow & (uint)(1 << (int)offset - 1)) != 0x0)
104+
{
105+
Console.WriteLine("DROPPED DUPLICATE <within window>: " + frame);
106+
return false;
107+
}
108+
else
109+
state.CounterWindow |= (uint)(1 << (int)offset - 1);
110+
}
111+
}
112+
PeerMessageCtr = state;
113+
if (frame.Message.Protocol == ProtocolType.SecureChannel && (SecureOpCodes?)frame.Message.OpCode == SecureOpCodes.MRPStandaloneAcknowledgement)
114+
return true; //Standalone Ack
115+
if (exchanges.TryGetValue(frame.Message.ExchangeID, out Exchange? exchange))
116+
exchange.Messages.Writer.TryWrite(frame);
117+
else
118+
Console.WriteLine("Unknown Exchange " + frame.Message.ExchangeID);
119+
return true;
120+
}
121+
122+
internal virtual bool HandleBehindWindow(ref MessageState state, Frame frame)
123+
{
124+
state.MaxMessageCounter = frame.Counter;
125+
state.CounterWindow = uint.MaxValue;
126+
return false;
127+
}
128+
69129
internal async Task DeleteExchange(Exchange exchange)
70130
{
71131
await Connection.CloseExchange(exchange);

MatterDotNet/Protocol/Sessions/SessionManager.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,5 @@ public static SessionParameter GetDefaultSessionParams()
107107
param.SpecificationVersion = 0;
108108
return param;
109109
}
110-
111-
internal static void SessionActive(ushort sessionID)
112-
{
113-
if (sessions.TryGetValue(sessionID, out SessionContext? context))
114-
{
115-
context.Timestamp = DateTime.Now;
116-
context.LastActive = DateTime.Now;
117-
}
118-
}
119110
}
120111
}

0 commit comments

Comments
 (0)