Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
PeerToPeerNetwork.cs
1using System;
2using System.IO;
4using System.Threading.Tasks;
5using System.Net;
7using Waher.Events;
9
11{
16 {
20 public const ushort DefaultPort = 0;
21
26
27 private readonly LinkedList<KeyValuePair<IPEndPoint, byte[]>> writeQueue = new LinkedList<KeyValuePair<IPEndPoint, byte[]>>();
28 private TcpListener tcpListener;
29 private UdpClient udpClient;
30 private IPEndPoint localEndpoint;
31 private IPEndPoint externalEndpoint;
32 private readonly int backlog;
33 private bool encapsulatePackets = true;
34 private bool isWriting = false;
35
43 public PeerToPeerNetwork(string ApplicationName, params ISniffer[] Sniffers)
45 {
46 }
47
57 public PeerToPeerNetwork(string ApplicationName, ushort LocalPort, ushort ExternalPort, params ISniffer[] Sniffers)
58 : this(ApplicationName, LocalPort, ExternalPort, DefaultBacklog, Sniffers)
59 {
60 }
61
72 public PeerToPeerNetwork(string ApplicationName, ushort LocalPort, ushort ExternalPort, int Backlog, params ISniffer[] Sniffers)
73 : base(new InternetGatewayRegistration[]
74 {
76 {
78 LocalPort = LocalPort,
79 ExternalPort = ExternalPort,
80 Tcp = true,
81 Udp = true
82 }
83 }, Sniffers)
84 {
85 this.backlog = Backlog;
86
87 this.tcpListener = null;
88 this.udpClient = null;
89
90 Task.Run(async () =>
91 {
92 try
93 {
94 await this.Start();
95 }
96 catch (Exception ex)
97 {
98 if (!this.disposed)
99 Log.Exception(ex);
100 }
101 });
102 }
103
107 public override async Task Start()
108 {
109 if (this.OnPublicNetwork())
110 {
111 try
112 {
113 ushort PublicPort;
114
115 this.tcpListener = new TcpListener(this.localAddress, this.ports[0].ExternalPort);
116 this.tcpListener.Start(this.backlog);
117
118 PublicPort = (ushort)((IPEndPoint)this.tcpListener.LocalEndpoint).Port;
119
120 this.localEndpoint = new IPEndPoint(this.localAddress, PublicPort);
121 this.externalEndpoint = new IPEndPoint(this.externalAddress, PublicPort);
122
123 this.udpClient = new UdpClient(this.localEndpoint.AddressFamily);
124 this.udpClient.Client.Bind(this.localEndpoint);
125
126 await this.SetState(PeerToPeerNetworkState.Ready);
127
128 this.AcceptTcpClients();
129 this.BeginReceiveUdp();
130 }
131 catch (Exception ex)
132 {
133 this.exception = ex;
134 await this.SetState(PeerToPeerNetworkState.Error);
135
136 this.tcpListener?.Stop();
137 this.tcpListener = null;
138
139 this.udpClient?.Dispose();
140 this.udpClient = null;
141 }
142 }
143
144 await base.Start();
145 }
146
147 private async void AcceptTcpClients()
148 {
149 try
150 {
151 while (!this.disposed && !(this.tcpListener is null))
152 {
153 try
154 {
155 TcpClient TcpClient;
156
157 try
158 {
159 TcpClient = await this.tcpListener.AcceptTcpClientAsync();
160 if (this.disposed)
161 return;
162 }
163 catch (InvalidOperationException)
164 {
165 await this.SetState(PeerToPeerNetworkState.Error);
166
167 this.tcpListener?.Stop();
168 this.tcpListener = null;
169
170 this.udpClient?.Dispose();
171 this.udpClient = null;
172
173 return;
174 }
175
176 if (!(TcpClient is null))
177 {
178 PeerConnection Connection = null;
179
180 try
181 {
182 BinaryTcpClient Client = new BinaryTcpClient(TcpClient, false);
183 Client.Bind(true);
184
185 Connection = new PeerConnection(Client, this,
186 (IPEndPoint)TcpClient.Client.RemoteEndPoint, this.encapsulatePackets);
187
188 await this.SetState(PeerToPeerNetworkState.Ready);
189
190 await this.PeerConnected(Connection);
191
192 Connection.Start();
193 }
194 catch (Exception)
195 {
196 if (!(Connection is null))
197 await Connection.DisposeAsync();
198 }
199 }
200 }
201 catch (SocketException)
202 {
203 // Ignore
204 }
205 catch (ObjectDisposedException)
206 {
207 // Ignore
208 }
209 catch (NullReferenceException)
210 {
211 // Ignore
212 }
213 catch (Exception ex)
214 {
215 if (!this.disposed)
216 Log.Exception(ex);
217 }
218 }
219 }
220 catch (Exception ex)
221 {
222 if (!this.disposed)
223 Log.Exception(ex);
224 }
225 }
226
230 public ushort DesiredLocalPort
231 {
232 get { return this.ports[0].LocalPort; }
233 set { this.ports[0].LocalPort = value; }
234 }
235
240 {
241 get { return this.ports[0].ExternalPort; }
242 set { this.ports[0].ExternalPort = value; }
243 }
244
248 public string ApplicationName
249 {
250 get { return this.ports[0].ApplicationName; }
251 }
252
256 public IPEndPoint ExternalEndpoint => this.externalEndpoint;
257
261 public IPEndPoint LocalEndpoint => this.localEndpoint;
262
268 {
269 get => this.encapsulatePackets;
270 set => this.encapsulatePackets = value;
271 }
272
279 protected override async Task BeforeRegistration(InternetGatewayRegistration Registration,
280 Dictionary<ushort, bool> TcpPortMapped, Dictionary<ushort, bool> UdpPortMapped)
281 {
282 try
283 {
284 do
285 {
286 this.tcpListener = new TcpListener(this.localAddress, Registration.LocalPort);
287 this.tcpListener.Start(this.backlog);
288
289 int i = ((IPEndPoint)this.tcpListener.LocalEndpoint).Port;
290
291 if (i < 0 || i > ushort.MaxValue ||
292 TcpPortMapped.ContainsKey((ushort)i) ||
293 UdpPortMapped.ContainsKey((ushort)i))
294 {
295 this.tcpListener.Stop();
296 this.tcpListener = null;
297 }
298 else
299 {
300 try
301 {
302 this.udpClient = new UdpClient(this.tcpListener.LocalEndpoint.AddressFamily);
303 this.udpClient.Client.Bind((IPEndPoint)this.tcpListener.LocalEndpoint);
304
305 Registration.LocalPort = (ushort)i;
306 if (Registration.ExternalPort == 0 ||
307 TcpPortMapped.ContainsKey((ushort)Registration.ExternalPort) ||
308 UdpPortMapped.ContainsKey((ushort)Registration.ExternalPort))
309 {
310 Registration.ExternalPort = Registration.LocalPort;
311 }
312 }
313 catch (Exception)
314 {
315 this.tcpListener.Stop();
316 this.tcpListener = null;
317 }
318 }
319 }
320 while (this.tcpListener is null);
321
322 this.localEndpoint = new IPEndPoint(this.localAddress, Registration.LocalPort);
323 this.externalEndpoint = new IPEndPoint(this.externalAddress, Registration.ExternalPort);
324
325 this.AcceptTcpClients();
326 this.BeginReceiveUdp();
327 }
328 catch (Exception ex)
329 {
330 this.exception = ex;
331 await this.SetState(PeerToPeerNetworkState.Error);
332 }
333 }
334
339 protected virtual Task PeerConnected(PeerConnection Connection)
340 {
341 return this.OnPeerConnected.Raise(this, Connection);
342 }
343
347 public event EventHandlerAsync<PeerConnection> OnPeerConnected = null;
348
352 public override Task DisposeAsync()
353 {
354 this.tcpListener?.Stop();
355 this.tcpListener = null;
356
357 this.udpClient?.Dispose();
358 this.udpClient = null;
359
360 return base.DisposeAsync();
361 }
362
369 public async Task<PeerConnection> ConnectToPeer(IPEndPoint RemoteEndPoint)
370 {
371 if (this.State != PeerToPeerNetworkState.Ready)
372 throw new IOException("Peer-to-peer network not ready.");
373
374 BinaryTcpClient Client = new BinaryTcpClient(false);
375 IPEndPoint RemoteEndPoint2 = RemoteEndPoint;
376
377 try
378 {
379 RemoteEndPoint2 = this.CheckLocalRemoteEndpoint(RemoteEndPoint);
380 await Client.ConnectAsync(RemoteEndPoint2.Address, RemoteEndPoint2.Port, true);
381 }
382 catch (Exception ex)
383 {
384 await Client.DisposeAsync();
385 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw();
386 }
387
388 PeerConnection Result = new PeerConnection(Client, this, RemoteEndPoint2, this.encapsulatePackets);
389
390 Result.StartIdleTimer();
391
392 return Result;
393 }
394
395 private async void BeginReceiveUdp() // Starts parallel task
396 {
397 try
398 {
399 while (!this.disposed)
400 {
401 UdpReceiveResult Data = await this.udpClient.ReceiveAsync();
402 if (!this.disposed)
403 await this.OnUdpDatagramReceived.Raise(this, new UdpDatagramEventArgs(Data.RemoteEndPoint, Data.Buffer));
404 }
405 }
406 catch (Exception ex)
407 {
408 if (!this.disposed)
409 Log.Exception(ex);
410 }
411 }
412
416 public event EventHandlerAsync<UdpDatagramEventArgs> OnUdpDatagramReceived = null;
417
423 public async Task SendUdp(IPEndPoint RemoteEndpoint, byte[] Datagram)
424 {
425 lock (this.writeQueue)
426 {
427 if (this.isWriting)
428 {
429 this.writeQueue.AddLast(new KeyValuePair<IPEndPoint, byte[]>(RemoteEndpoint, Datagram));
430 return;
431 }
432 else
433 this.isWriting = true;
434 }
435
436 try
437 {
438 while (!this.disposed && !(Datagram is null))
439 {
440 await this.udpClient.SendAsync(Datagram, Datagram.Length, RemoteEndpoint);
441
442 await this.OnUdpDatagramSent.Raise(this, new UdpDatagramEventArgs(RemoteEndpoint, Datagram));
443
444 lock (this.writeQueue)
445 {
446 if (!(this.writeQueue.First is null))
447 {
448 KeyValuePair<IPEndPoint, byte[]> Rec = this.writeQueue.First.Value;
449 this.writeQueue.RemoveFirst();
450
451 RemoteEndpoint = Rec.Key;
452 Datagram = Rec.Value;
453 }
454 else
455 {
456 this.isWriting = false;
457 Datagram = null;
458 }
459 }
460 }
461 }
462 catch (Exception ex)
463 {
464 lock (this.writeQueue)
465 {
466 this.isWriting = false;
467 this.writeQueue.Clear();
468 }
469
470 if (!this.disposed)
471 Log.Exception(ex);
472 }
473 }
474
478 public event EventHandlerAsync<UdpDatagramEventArgs> OnUdpDatagramSent = null;
479 }
480}
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...
void Bind()
Binds to a TcpClient that was already connected when provided to the constructor.
virtual Task DisposeAsync()
Disposes of the object asynchronously. The underlying TcpClient is either disposed directly,...
Task< bool > ConnectAsync(string Host, int Port)
Connects to a host using TCP.
Implements a binary TCP Server. The server adapts to network changes, maintains a list of current con...
const int DefaultC2CConnectionBacklog
Default Client-to-Client Connection backlog (10).
Represents a registraing in an UPnP-compatible Internet Gateway.
string ApplicationName
Name of application to be registered.
Manages registration of TCP and UDP ports in an Internet Gateway
Exception Exception
In case State=PeerToPeerNetworkState.Error, this exception object contains details about the error.
IPEndPoint CheckLocalRemoteEndpoint(IPEndPoint RemoteEndPoint)
Checks if a remote endpoint resides in the internal network, and if so, replaces it with the correspo...
bool OnPublicNetwork()
If the machine is on a public network.
PeerToPeerNetworkState State
Current state of the peer-to-peer network object.
Manages a peer-to-peer network that can receive connections from outside of a NAT-enabled firewall.
const ushort DefaultPort
Default desired port number. (0 = any port number.)
EventHandlerAsync< PeerConnection > OnPeerConnected
Event raised when a new peer has connected.
bool EncapsulatePackets
If packets are to be encapsulated and delivered as ordered units (true), or if fragmentation in the T...
virtual Task PeerConnected(PeerConnection Connection)
Called when a new peer has connected.
PeerToPeerNetwork(string ApplicationName, params ISniffer[] Sniffers)
Manages a peer-to-peer network that can receive connections from outside of a NAT-enabled firewall.
PeerToPeerNetwork(string ApplicationName, ushort LocalPort, ushort ExternalPort, int Backlog, params ISniffer[] Sniffers)
Manages a peer-to-peer network that can receive connections from outside of a NAT-enabled firewall.
async Task< PeerConnection > ConnectToPeer(IPEndPoint RemoteEndPoint)
Connects to a peer in the peer-to-peer network. If the remote end point resides behind the same firew...
EventHandlerAsync< UdpDatagramEventArgs > OnUdpDatagramReceived
Event raised when an incoming UDP datagram has been received.
PeerToPeerNetwork(string ApplicationName, ushort LocalPort, ushort ExternalPort, params ISniffer[] Sniffers)
Manages a peer-to-peer network that can receive connections from outside of a NAT-enabled firewall.
async Task SendUdp(IPEndPoint RemoteEndpoint, byte[] Datagram)
Sends an UDP datagram to a remote destination.
EventHandlerAsync< UdpDatagramEventArgs > OnUdpDatagramSent
Event raised when an outgoing UDP datagram has been sent.
ushort DesiredLocalPort
Desired local port number. If 0, a dynamic port number will be assigned.
IPEndPoint ExternalEndpoint
External IP Endpoint.
override Task DisposeAsync()
IDisposable.Dispose
ushort DesiredExternalPort
Desired external port number. If 0, a dynamic port number will be assigned.
override async Task Start()
Starts searching for Internet Gateways. Once found, ports will be registered.
const int DefaultBacklog
Default connection backlog (10).
override async Task BeforeRegistration(InternetGatewayRegistration Registration, Dictionary< ushort, bool > TcpPortMapped, Dictionary< ushort, bool > UdpPortMapped)
is called before performing a registration.
Event arguments for UDP Datagram events.
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
Definition: ISniffer.cs:10
PeerToPeerNetworkState
State of Peer-to-peer network.