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;
3using System.Collections.Generic;
4using System.Threading.Tasks;
5using System.Net;
6using System.Net.Sockets;
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)
74 {
76 LocalPort = LocalPort,
77 ExternalPort = ExternalPort,
78 Tcp = true,
79 Udp = true
80 } }, Sniffers)
81 {
82 this.backlog = Backlog;
83
84 this.tcpListener = null;
85 this.udpClient = null;
86
87 Task.Run(async () =>
88 {
89 try
90 {
91 await this.Start();
92 }
93 catch (Exception ex)
94 {
95 Log.Exception(ex);
96 }
97 });
98 }
99
103 public override async Task Start()
104 {
105 if (this.OnPublicNetwork())
106 {
107 try
108 {
109 ushort PublicPort;
110
111 this.tcpListener = new TcpListener(this.localAddress, this.ports[0].ExternalPort);
112 this.tcpListener.Start(this.backlog);
113
114 PublicPort = (ushort)((IPEndPoint)this.tcpListener.LocalEndpoint).Port;
115
116 this.localEndpoint = new IPEndPoint(this.localAddress, PublicPort);
117 this.externalEndpoint = new IPEndPoint(this.externalAddress, PublicPort);
118
119 this.udpClient = new UdpClient(this.localEndpoint.AddressFamily);
120 this.udpClient.Client.Bind(this.localEndpoint);
121
122 await this.SetState(PeerToPeerNetworkState.Ready);
123
124 this.AcceptTcpClients();
125 this.BeginReceiveUdp();
126 }
127 catch (Exception ex)
128 {
129 this.exception = ex;
130 await this.SetState(PeerToPeerNetworkState.Error);
131
132 this.tcpListener?.Stop();
133 this.tcpListener = null;
134
135 this.udpClient?.Dispose();
136 this.udpClient = null;
137 }
138 }
139
140 await base.Start();
141 }
142
143 private async void AcceptTcpClients()
144 {
145 try
146 {
147 while (!this.disposed && !(this.tcpListener is null))
148 {
149 try
150 {
151 TcpClient TcpClient;
152
153 try
154 {
155 TcpClient = await this.tcpListener.AcceptTcpClientAsync();
156 if (this.disposed)
157 return;
158 }
159 catch (InvalidOperationException)
160 {
161 await this.SetState(PeerToPeerNetworkState.Error);
162
163 this.tcpListener?.Stop();
164 this.tcpListener = null;
165
166 this.udpClient?.Dispose();
167 this.udpClient = null;
168
169 return;
170 }
171
172 if (!(TcpClient is null))
173 {
174 PeerConnection Connection = null;
175
176 try
177 {
178 BinaryTcpClient Client = new BinaryTcpClient(TcpClient, false);
179 Client.Bind(true);
180
181 Connection = new PeerConnection(Client, this,
182 (IPEndPoint)TcpClient.Client.RemoteEndPoint, this.encapsulatePackets);
183
184 await this.SetState(PeerToPeerNetworkState.Ready);
185
186 await this.PeerConnected(Connection);
187
188 Connection.Start();
189 }
190 catch (Exception)
191 {
192 if (!(Connection is null))
193 await Connection.DisposeAsync();
194 }
195 }
196 }
197 catch (SocketException)
198 {
199 // Ignore
200 }
201 catch (ObjectDisposedException)
202 {
203 // Ignore
204 }
205 catch (NullReferenceException)
206 {
207 // Ignore
208 }
209 catch (Exception ex)
210 {
211 Log.Exception(ex);
212 }
213 }
214 }
215 catch (Exception ex)
216 {
217 if (this.disposed)
218 return;
219
220 Log.Exception(ex);
221 }
222 }
223
227 public ushort DesiredLocalPort
228 {
229 get { return this.ports[0].LocalPort; }
230 set { this.ports[0].LocalPort = value; }
231 }
232
237 {
238 get { return this.ports[0].ExternalPort; }
239 set { this.ports[0].ExternalPort = value; }
240 }
241
245 public string ApplicationName
246 {
247 get { return this.ports[0].ApplicationName; }
248 }
249
253 public IPEndPoint ExternalEndpoint => this.externalEndpoint;
254
258 public IPEndPoint LocalEndpoint => this.localEndpoint;
259
265 {
266 get => this.encapsulatePackets;
267 set => this.encapsulatePackets = value;
268 }
269
276 protected override async Task BeforeRegistration(InternetGatewayRegistration Registration,
277 Dictionary<ushort, bool> TcpPortMapped, Dictionary<ushort, bool> UdpPortMapped)
278 {
279 try
280 {
281 do
282 {
283 this.tcpListener = new TcpListener(this.localAddress, Registration.LocalPort);
284 this.tcpListener.Start(this.backlog);
285
286 int i = ((IPEndPoint)this.tcpListener.LocalEndpoint).Port;
287
288 if (i < 0 || i > ushort.MaxValue ||
289 TcpPortMapped.ContainsKey((ushort)i) ||
290 UdpPortMapped.ContainsKey((ushort)i))
291 {
292 this.tcpListener.Stop();
293 this.tcpListener = null;
294 }
295 else
296 {
297 try
298 {
299 this.udpClient = new UdpClient(this.tcpListener.LocalEndpoint.AddressFamily);
300 this.udpClient.Client.Bind((IPEndPoint)this.tcpListener.LocalEndpoint);
301
302 Registration.LocalPort = (ushort)i;
303 if (Registration.ExternalPort == 0 ||
304 TcpPortMapped.ContainsKey((ushort)Registration.ExternalPort) ||
305 UdpPortMapped.ContainsKey((ushort)Registration.ExternalPort))
306 {
307 Registration.ExternalPort = Registration.LocalPort;
308 }
309 }
310 catch (Exception)
311 {
312 this.tcpListener.Stop();
313 this.tcpListener = null;
314 }
315 }
316 }
317 while (this.tcpListener is null);
318
319 this.localEndpoint = new IPEndPoint(this.localAddress, Registration.LocalPort);
320 this.externalEndpoint = new IPEndPoint(this.externalAddress, Registration.ExternalPort);
321
322 this.AcceptTcpClients();
323 this.BeginReceiveUdp();
324 }
325 catch (Exception ex)
326 {
327 this.exception = ex;
328 await this.SetState(PeerToPeerNetworkState.Error);
329 }
330 }
331
336 protected virtual Task PeerConnected(PeerConnection Connection)
337 {
338 return this.OnPeerConnected.Raise(this, Connection);
339 }
340
344 public event EventHandlerAsync<PeerConnection> OnPeerConnected = null;
345
349 public override Task DisposeAsync()
350 {
351 this.tcpListener?.Stop();
352 this.tcpListener = null;
353
354 this.udpClient?.Dispose();
355 this.udpClient = null;
356
357 return base.DisposeAsync();
358 }
359
366 public async Task<PeerConnection> ConnectToPeer(IPEndPoint RemoteEndPoint)
367 {
368 if (this.State != PeerToPeerNetworkState.Ready)
369 throw new IOException("Peer-to-peer network not ready.");
370
371 BinaryTcpClient Client = new BinaryTcpClient(false);
372 IPEndPoint RemoteEndPoint2 = RemoteEndPoint;
373
374 try
375 {
376 RemoteEndPoint2 = this.CheckLocalRemoteEndpoint(RemoteEndPoint);
377 await Client.ConnectAsync(RemoteEndPoint2.Address, RemoteEndPoint2.Port, true);
378 }
379 catch (Exception ex)
380 {
381 Client.Dispose();
382 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw();
383 }
384
385 PeerConnection Result = new PeerConnection(Client, this, RemoteEndPoint2, this.encapsulatePackets);
386
387 Result.StartIdleTimer();
388
389 return Result;
390 }
391
392 private async void BeginReceiveUdp() // Starts parallel task
393 {
394 try
395 {
396 while (!this.disposed)
397 {
398 UdpReceiveResult Data = await this.udpClient.ReceiveAsync();
399 if (!this.disposed)
400 await this.OnUdpDatagramReceived.Raise(this, new UdpDatagramEventArgs(Data.RemoteEndPoint, Data.Buffer));
401 }
402 }
403 catch (Exception ex)
404 {
405 Log.Exception(ex);
406 }
407 }
408
412 public event EventHandlerAsync<UdpDatagramEventArgs> OnUdpDatagramReceived = null;
413
419 public async Task SendUdp(IPEndPoint RemoteEndpoint, byte[] Datagram)
420 {
421 lock (this.writeQueue)
422 {
423 if (this.isWriting)
424 {
425 this.writeQueue.AddLast(new KeyValuePair<IPEndPoint, byte[]>(RemoteEndpoint, Datagram));
426 return;
427 }
428 else
429 this.isWriting = true;
430 }
431
432 try
433 {
434 while (!this.disposed && !(Datagram is null))
435 {
436 await this.udpClient.SendAsync(Datagram, Datagram.Length, RemoteEndpoint);
437
438 await this.OnUdpDatagramSent.Raise(this, new UdpDatagramEventArgs(RemoteEndpoint, Datagram));
439
440 lock (this.writeQueue)
441 {
442 if (!(this.writeQueue.First is null))
443 {
444 KeyValuePair<IPEndPoint, byte[]> Rec = this.writeQueue.First.Value;
445 this.writeQueue.RemoveFirst();
446
447 RemoteEndpoint = Rec.Key;
448 Datagram = Rec.Value;
449 }
450 else
451 {
452 this.isWriting = false;
453 Datagram = null;
454 }
455 }
456 }
457 }
458 catch (Exception ex)
459 {
460 lock (this.writeQueue)
461 {
462 this.isWriting = false;
463 this.writeQueue.Clear();
464 }
465
466 Log.Exception(ex);
467 }
468 }
469
473 public event EventHandlerAsync<UdpDatagramEventArgs> OnUdpDatagramSent = null;
474 }
475}
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
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:1647
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.
Task< bool > ConnectAsync(string Host, int Port)
Connects to a host using TCP.
virtual void Dispose()
Disposes of the object. The underlying TcpClient is either disposed directly, or when asynchronous op...
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:11
PeerToPeerNetworkState
State of Peer-to-peer network.