Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
PeerConnection.cs
1using System;
3using System.Net;
4using System.Threading;
5using System.Threading.Tasks;
6using Waher.Events;
7
9{
14 {
15 private byte[] packetBuffer = null;
16 private readonly PeerToPeerNetwork network;
17 private IPEndPoint remoteEndpoint;
18 private BinaryTcpClient tcpConnection;
19 private EventHandlerAsync resynchCallback;
20 private object stateObject = null;
21 private int readState = 0;
22 private int packetSize = 0;
23 private ushort outgoingPacketNumber = 0;
24 private int offset = 0;
25 private int packetPos = 0;
26 private bool closed = false;
27 private bool disposed = false;
28 private readonly bool encapsulatePackets;
29
30 internal PeerConnection(BinaryTcpClient TcpConnection, PeerToPeerNetwork Network, IPEndPoint RemoteEndpoint,
31 bool EncapsulatePackets)
32 {
33 this.network = Network;
34 this.remoteEndpoint = RemoteEndpoint;
35 this.tcpConnection = TcpConnection;
36 this.encapsulatePackets = EncapsulatePackets;
37
38 this.tcpConnection.OnDisconnected += this.TcpConnection_OnDisconnected;
39 this.tcpConnection.OnError += this.TcpConnection_OnError;
40 this.tcpConnection.OnReceived += this.TcpConnection_OnReceived;
41 this.tcpConnection.OnSent += this.TcpConnection_OnSent;
42 }
43
44 private async Task TcpConnection_OnSent(object Sender, bool ConstantBuffer, byte[] Buffer, int Offset, int Count)
45 {
46 this.lastTcpPacket = DateTime.Now;
47
49 if (!(h is null))
50 {
51 try
52 {
53 await h(this, ConstantBuffer, Buffer, Offset, Count);
54 }
55 catch (Exception ex)
56 {
57 Log.Exception(ex);
58 }
59 }
60 }
61
62 private async Task<bool> TcpConnection_OnReceived(object Sender, bool ConstantBuffer, byte[] Buffer, int Offset, int Count)
63 {
64 bool Continue = true;
65
66 this.lastTcpPacket = DateTime.Now;
67 this.resynchCallback = null;
68
69 if (this.encapsulatePackets)
70 {
71 int NrLeft;
72 byte b;
73
74 while (Count-- > 0 && Continue && !this.disposed)
75 {
76 switch (this.readState)
77 {
78 case 0:
79 b = Buffer[Offset++];
80 this.packetSize |= (b & 127) << this.offset;
81 this.offset += 7;
82 if ((b & 128) == 0)
83 {
84 this.packetBuffer = new byte[this.packetSize];
85 this.packetPos = 0;
86 this.readState = 1;
87 }
88 break;
89
90 case 1:
91 NrLeft = Math.Min(Count, this.packetSize - this.packetPos);
92 System.Buffer.BlockCopy(Buffer, Offset, this.packetBuffer, this.packetPos, NrLeft);
93 Offset += NrLeft;
94 this.packetPos += NrLeft;
95
96 if (this.packetPos >= this.packetSize)
97 {
98 Continue = await this.OnPacketReceived();
99
100 this.readState = 0;
101 this.packetSize = 0;
102 this.offset = 0;
103 this.packetBuffer = null;
104 }
105 break;
106
107 default:
108 Count = 0;
109 break;
110 }
111 }
112 }
113 else
114 {
115 this.packetSize = Count;
116 this.packetBuffer = new byte[Count];
117 System.Buffer.BlockCopy(Buffer, Offset, this.packetBuffer, 0, Count);
118 Continue = await this.OnPacketReceived();
119 }
120
121 return Continue;
122 }
123
124 private async Task<bool> OnPacketReceived()
125 {
127 if (!(h is null))
128 {
129 try
130 {
131 return await h(this, false, this.packetBuffer, 0, this.packetSize);
132 }
133 catch (Exception ex)
134 {
135 Log.Exception(ex);
136 }
137 }
138
139 return true;
140 }
141
142 private Task TcpConnection_OnError(object _, Exception _2)
143 {
144 return this.Closed();
145 }
146
147 private Task TcpConnection_OnDisconnected(object Sender, EventArgs e)
148 {
149 return this.Closed();
150 }
151
155 public void Start()
156 {
157 this.Start(null);
158 }
159
165 public void Start(EventHandlerAsync ResynchCallback)
166 {
167 this.readState = 0;
168 this.packetSize = 0;
169 this.offset = 0;
170 this.resynchCallback = ResynchCallback;
171 this.tcpConnection?.Continue();
172 }
173
177 public BinaryTcpClient Tcp => this.tcpConnection;
178
182 public PeerToPeerNetwork Network => this.network;
183
187 public IPEndPoint RemoteEndpoint
188 {
189 get => this.remoteEndpoint;
190 internal set => this.remoteEndpoint = value;
191 }
192
196 [Obsolete("Use DisposeAsync()")]
197 public void Dispose()
198 {
199 this.DisposeAsync().Wait();
200 }
201
205 public async Task DisposeAsync()
206 {
207 this.disposed = true;
208
209 this.idleTimer?.Dispose();
210 this.idleTimer = null;
211
212 if (!(this.tcpConnection is null))
213 {
214 await this.tcpConnection.DisposeAsync();
215 this.tcpConnection = null;
216 }
217
218 await this.Closed();
219 }
220
226 [Obsolete("Use an overload with a ConstantBuffer argument. This increases performance, as the buffer will not be unnecessarily cloned if queued.")]
227 public Task SendTcp(byte[] Packet)
228 {
229 return this.SendTcp(false, Packet);
230 }
231
239 public Task SendTcp(bool ConstantBuffer, byte[] Packet)
240 {
241 return this.SendTcp(ConstantBuffer, Packet, null, null);
242 }
243
251 [Obsolete("Use an overload with a ConstantBuffer argument. This increases performance, as the buffer will not be unnecessarily cloned if queued.")]
252 public Task SendTcp(byte[] Packet, EventHandlerAsync<DeliveryEventArgs> Callback, object State)
253 {
254 return this.SendTcp(false, Packet, Callback, State);
255 }
256
266 public Task SendTcp(bool ConstantBuffer, byte[] Packet, EventHandlerAsync<DeliveryEventArgs> Callback, object State)
267 {
268 if (this.disposed)
269 return Task.CompletedTask;
270
271 byte[] EncodedPacket = this.EncodePacket(Packet, false, out bool ConstantBuffer2);
272 return this.tcpConnection.SendAsync(ConstantBuffer || ConstantBuffer2, EncodedPacket, Callback, State);
273 }
274
275 private byte[] EncodePacket(byte[] Packet, bool IncludePacketNumber, out bool ConstantBuffer)
276 {
277 if (!this.encapsulatePackets)
278 {
279 ConstantBuffer = false;
280 return Packet;
281 }
282
283 ushort PacketNr;
284 int i = Packet.Length;
285 int j = 0;
286 int c = 1;
287 byte b;
288
289 i >>= 7;
290 while (i > 0)
291 {
292 c++;
293 i >>= 7;
294 }
295
296 if (IncludePacketNumber)
297 c += 2;
298
299 i = Packet.Length;
300
301 byte[] Packet2 = new byte[c + i];
302 Buffer.BlockCopy(Packet, 0, Packet2, c, i);
303 ConstantBuffer = true;
304
305 do
306 {
307 b = (byte)(i & 127);
308 i >>= 7;
309 if (i > 0)
310 b |= 128;
311
312 Packet2[j++] = b;
313 }
314 while (i > 0);
315
316 if (IncludePacketNumber)
317 {
318 PacketNr = ++this.outgoingPacketNumber;
319
320 Packet2[j++] = (byte)PacketNr;
321 Packet2[j++] = (byte)(PacketNr >> 8);
322 }
323
324 return Packet2;
325 }
326
336 public Task SendUdp(byte[] Packet, int IncludeNrPreviousPackets)
337 {
338 byte[] EncodedPacket = this.EncodePacket(Packet, true, out bool _);
339
340 lock (this.historicPackets)
341 {
342 byte[] ToSend;
343 int j, i = 0;
344 int c = EncodedPacket.Length;
345
346 if (IncludeNrPreviousPackets == 0)
347 ToSend = EncodedPacket;
348 else
349 {
350 foreach (byte[] Packet2 in this.historicPackets)
351 {
352 c += Packet2.Length;
353 i++;
354 if (i >= IncludeNrPreviousPackets)
355 break;
356 }
357
358 ToSend = new byte[c];
359 j = EncodedPacket.Length;
360 Buffer.BlockCopy(EncodedPacket, 0, ToSend, 0, j);
361
362 i = 0;
363 foreach (byte[] Packet2 in this.historicPackets)
364 {
365 Buffer.BlockCopy(Packet2, 0, ToSend, j, Packet2.Length);
366 j += Packet2.Length;
367 i++;
368 if (i >= IncludeNrPreviousPackets)
369 break;
370 }
371 }
372
373 this.historicPackets.AddFirst(EncodedPacket);
374
375 if (this.nrHistoricPackets >= IncludeNrPreviousPackets)
376 this.historicPackets.RemoveLast(); // Doesn't reduce the size to INcludeNrPreviousPackets, but keeps list at the largest requested number, to date.
377 else
378 this.nrHistoricPackets++;
379
380 return this.network.SendUdp(this.remoteEndpoint, ToSend);
381 }
382 }
383
384 private int nrHistoricPackets = 0;
385 private readonly LinkedList<byte[]> historicPackets = new LinkedList<byte[]>();
386
391
395 public bool Paused => this.tcpConnection.Paused;
396
400 public void Continue()
401 {
402 this.tcpConnection.Continue();
403 }
404
409
410 private async Task Closed()
411 {
412 if (!this.closed)
413 {
414 this.closed = true;
415
416 if (!(this.resynchCallback is null))
417 {
418 await this.resynchCallback.Raise(this, EventArgs.Empty, false);
419 await this.DisposeAsync();
420 }
421 else
422 await this.RaiseOnClosed();
423 }
424 }
425
426 private Task RaiseOnClosed()
427 {
428 return this.OnClosed.Raise(this, EventArgs.Empty);
429 }
430
434 public event EventHandlerAsync OnClosed = null;
435
439 public object StateObject
440 {
441 get => this.stateObject;
442 set => this.stateObject = value;
443 }
444
445 internal async Task UdpDatagramReceived(object _, UdpDatagramEventArgs e)
446 {
447 if (this.encapsulatePackets)
448 {
449 LinkedList<KeyValuePair<ushort, byte[]>> LostPackets = null;
450 byte[] FirstPacket = null;
451 ushort FirstPacketNr = 0;
452 ushort PacketNr;
453 byte[] Packet;
454 byte[] Data = e.Data;
455 int Len = Data.Length;
456 int Pos = 0;
457 int PacketLen;
458 int Offset;
459 byte b;
460
461 lock (this.udpReceiveLock)
462 {
463 while (Pos < Len)
464 {
465 b = Data[Pos++];
466 PacketLen = (b & 127);
467 Offset = 7;
468 while (Pos < Len && (b & 128) != 0)
469 {
470 b = Data[Pos++];
471 PacketLen |= (b & 127) << Offset;
472 Offset += 7;
473 }
474
475 if (Pos + 2 > Len)
476 break;
477
478 PacketNr = Data[Pos++];
479 PacketNr |= (ushort)(Data[Pos++] << 8);
480
481 if (Pos + PacketLen > Len)
482 break;
483
484 Packet = new byte[PacketLen];
485 Buffer.BlockCopy(Data, Pos, Packet, 0, PacketLen);
486 Pos += PacketLen;
487
488 if ((short)(PacketNr - this.lastReceivedPacket) > 0)
489 {
490 if (FirstPacket is null)
491 {
492 FirstPacket = Packet;
493 FirstPacketNr = PacketNr;
494 }
495 else
496 {
497 LostPackets ??= new LinkedList<KeyValuePair<ushort, byte[]>>();
498 LostPackets.AddFirst(new KeyValuePair<ushort, byte[]>(PacketNr, Packet)); // Reverse order
499 }
500 }
501 }
502
503 if (!(FirstPacket is null))
504 this.lastReceivedPacket = FirstPacketNr;
505 }
506
508 if (!(h is null))
509 {
510 if (!(LostPackets is null))
511 {
512 foreach (KeyValuePair<ushort, byte[]> P in LostPackets)
513 {
514 try
515 {
516 await h(this, true, P.Value, 0, P.Value.Length);
517 }
518 catch (Exception ex)
519 {
520 Log.Exception(ex);
521 }
522 }
523 }
524
525 if (!(FirstPacket is null))
526 {
527 try
528 {
529 await h(this, true, FirstPacket, 0, FirstPacket.Length);
530 }
531 catch (Exception ex)
532 {
533 Log.Exception(ex);
534 }
535 }
536 }
537 }
538 else
539 {
540 byte[] Data = e.Data;
541 int Len = Data.Length;
542 byte[] Packet = new byte[Len];
543
544 Buffer.BlockCopy(Data, 0, Packet, 0, Len);
545
547 if (!(h is null))
548 {
549 try
550 {
551 await h(this, true, Packet, 0, Packet.Length);
552 }
553 catch (Exception ex)
554 {
555 Log.Exception(ex);
556 }
557 }
558 }
559 }
560
561 private ushort lastReceivedPacket = 0;
562 private readonly object udpReceiveLock = new object();
563
564 internal void StartIdleTimer()
565 {
566 this.idleTimer = new Timer(this.IdleTimerCallback, null, 5000, 5000);
567 }
568
569 private async void IdleTimerCallback(object P)
570 {
571 try
572 {
573 if ((DateTime.Now - this.lastTcpPacket).TotalSeconds > 10)
574 {
575 try
576 {
577 await this.SendTcp(true, Array.Empty<byte>());
578 }
579 catch (Exception)
580 {
581 try
582 {
583 await this.Closed();
584 await this.DisposeAsync();
585 }
586 catch (Exception ex)
587 {
588 Log.Exception(ex);
589 }
590 }
591 }
592 }
593 catch (Exception ex)
594 {
595 Log.Exception(ex);
596 }
597 }
598
599 private Timer idleTimer = null;
600 private DateTime lastTcpPacket = DateTime.Now;
601
602 }
603}
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1657
Implements a binary TCP Client, by encapsulating a TcpClient. It also makes the use of TcpClient safe...
Task< bool > SendAsync(byte[] Packet)
Sends a binary packet.
void Continue()
Continues reading from the socket, if paused in an event handler.
bool Paused
If the reading is paused.
virtual Task DisposeAsync()
Disposes of the object asynchronously. The underlying TcpClient is either disposed directly,...
BinaryTcpClient Tcp
Underlying TCP connection
PeerToPeerNetwork Network
Peer-to-peer network.
object StateObject
State object that applications can use to attach information to a connection.
BinaryDataWrittenEventHandler OnSent
Event raised when a packet has been sent.
void Start(EventHandlerAsync ResynchCallback)
Starts receiving on the connection.
IPEndPoint RemoteEndpoint
Remote endpoint.
Task SendTcp(bool ConstantBuffer, byte[] Packet)
Sends a packet to the peer at the other side of the TCP connection. Transmission is done asynchronous...
void Start()
Starts receiving on the connection.
void Continue()
Continues a paused connection.
bool Paused
If reading has been paused.
Task SendTcp(byte[] Packet)
Sends a packet to the peer at the other side of the TCP connection. Transmission is done asynchronous...
Task SendTcp(byte[] Packet, EventHandlerAsync< DeliveryEventArgs > Callback, object State)
Sends a packet to the peer at the other side of the TCP connection. Transmission is done asynchronous...
async Task DisposeAsync()
IDisposable.Dispose
BinaryDataReadEventHandler OnReceived
Event received when binary data has been received.
Task SendTcp(bool ConstantBuffer, byte[] Packet, EventHandlerAsync< DeliveryEventArgs > Callback, object State)
Sends a packet to the peer at the other side of the TCP connection. Transmission is done asynchronous...
Task SendUdp(byte[] Packet, int IncludeNrPreviousPackets)
Sends a packet to a peer using UDP. Transmission is done asynchronously and is buffered if a sending ...
EventHandlerAsync OnClosed
Event raised when a connection has been closed for some reason.
Manages a peer-to-peer network that can receive connections from outside of a NAT-enabled firewall.
Event arguments for UDP Datagram events.
Interface for asynchronously disposable objects.
Definition: ImplTypes.g.cs:58
delegate Task EventHandlerAsync(object Sender, EventArgs e)
Asynchronous version of EventArgs.
delegate Task< bool > BinaryDataReadEventHandler(object Sender, bool ConstantBuffer, byte[] Buffer, int Offset, int Count)
Event handler for binary packet events.
delegate Task BinaryDataWrittenEventHandler(object Sender, bool ConstantBuffer, byte[] Buffer, int Offset, int Count)
Event handler for binary packet events.