Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ClusterEndpoint.cs
1using System;
3using System.Net;
4using System.Net.NetworkInformation;
6using System.Reflection;
8using System.Text;
9using System.Threading;
10using System.Threading.Tasks;
11using Waher.Events;
20using Waher.Security;
21
23{
28 {
29 private static readonly Dictionary<Type, ObjectInfo> objectInfo = new Dictionary<Type, ObjectInfo>();
30 private static Dictionary<Type, IProperty> propertyTypes = null;
31 private static ObjectInfo rootObject;
32
33 private readonly LinkedList<ClusterUdpClient> outgoing = new LinkedList<ClusterUdpClient>();
34 private readonly LinkedList<ClusterUdpClient> incoming = new LinkedList<ClusterUdpClient>();
35 private readonly IPEndPoint destination;
36 internal readonly byte[] key;
37 internal Aes aes;
38 internal Cache<string, object> currentStatus;
39 private Scheduler scheduler;
40 private readonly Random rnd = new Random();
41 private readonly Dictionary<IPEndPoint, object> remoteStatus;
42 internal readonly Dictionary<string, LockInfo> lockedResources = new Dictionary<string, LockInfo>();
43 private object localStatus;
44 private Timer aliveTimer;
45 internal bool shuttingDown = false;
46 private readonly bool internalScheduler;
47 private ManualResetEvent shutDown = new ManualResetEvent(false);
48
56 public ClusterEndpoint(IPAddress MulticastAddress, int Port, string SharedSecret,
57 params ISniffer[] Sniffers)
58 : this(MulticastAddress, Port, Encoding.UTF8.GetBytes(SharedSecret), Sniffers)
59 {
60 }
61
69 public ClusterEndpoint(IPAddress MulticastAddress, int Port, byte[] SharedSecret, params ISniffer[] Sniffers)
70 : base(false, Sniffers)
71 {
72 ClusterUdpClient ClusterUdpClient;
73 UdpClient Client;
74
75 if (MulticastAddress.AddressFamily != AddressFamily.InterNetwork)
76 throw new ArgumentException("Cluster communication must be done using IPv4.", nameof(MulticastAddress));
77
78 if (propertyTypes is null)
79 {
80 Init();
81 rootObject = GetObjectInfo(typeof(object));
82 }
83
84 this.aes = Aes.Create();
85 this.aes.BlockSize = 128;
86 this.aes.KeySize = 256;
87 this.aes.Mode = CipherMode.CBC;
88 this.aes.Padding = PaddingMode.None;
89
90 this.key = Hashes.ComputeSHA256Hash(SharedSecret);
91 this.destination = new IPEndPoint(MulticastAddress, Port);
92
93 this.localStatus = null;
94 this.remoteStatus = new Dictionary<IPEndPoint, object>();
95 this.currentStatus = new Cache<string, object>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromSeconds(30), true);
96 this.currentStatus.Removed += this.CurrentStatus_Removed;
97
98 if (Types.TryGetModuleParameter("Scheduler", out Scheduler Scheduler))
99 {
100 this.scheduler = Scheduler;
101 this.internalScheduler = false;
102 }
103 else
104 {
105 this.scheduler = new Scheduler();
106 this.internalScheduler = true;
107 }
108
109 foreach (NetworkInterface Interface in NetworkInterface.GetAllNetworkInterfaces())
110 {
111 if (Interface.OperationalStatus != OperationalStatus.Up)
112 continue;
113
114 switch (Interface.NetworkInterfaceType)
115 {
116 case NetworkInterfaceType.Loopback:
117 continue;
118 }
119
120 IPInterfaceProperties Properties = Interface.GetIPProperties();
121
122 foreach (UnicastIPAddressInformation UnicastAddress in Properties.UnicastAddresses)
123 {
124 IPAddress Address = UnicastAddress.Address;
125 AddressFamily AddressFamily = Address.AddressFamily;
126 bool IsLoopback = Interface.NetworkInterfaceType == NetworkInterfaceType.Loopback;
127
128 if (Address.AddressFamily != MulticastAddress.AddressFamily)
129 continue;
130
131 if (!IsLoopback)
132 {
133 Client = null;
134
135 try
136 {
137 Client = new UdpClient(AddressFamily)
138 {
139 //DontFragment = true,
140 ExclusiveAddressUse = true,
141 MulticastLoopback = true,
142 EnableBroadcast = true,
143 Ttl = 30
144 };
145
146 this.Information("Binding to " + Address);
147
148 Client.Client.Bind(new IPEndPoint(Address, 0));
149
150 if (IsMulticastAddress(MulticastAddress) && Interface.SupportsMulticast)
151 {
152 this.Information("Joining Multicast address " + MulticastAddress);
153
154 try
155 {
156 Client.JoinMulticastGroup(MulticastAddress);
157 }
158 catch (Exception ex)
159 {
160 Log.Exception(ex);
161 }
162 }
163 else
164 this.Warning("Address provided is not a multi-cast address.");
165 }
166 catch (NotSupportedException)
167 {
168 Client?.Dispose();
169 continue;
170 }
171 catch (Exception ex)
172 {
173 Client?.Dispose();
174 Log.Exception(ex);
175 continue;
176 }
177
178 ClusterUdpClient = new ClusterUdpClient(this, Client, Address);
179 ClusterUdpClient.BeginReceive();
180
181 this.outgoing.AddLast(ClusterUdpClient);
182
183 Client = null;
184
185 try
186 {
187 Client = new UdpClient(AddressFamily)
188 {
189 ExclusiveAddressUse = false,
190 };
191
192 Client.Client.Bind(new IPEndPoint(Address, Port));
193 }
194 catch (NotSupportedException)
195 {
196 Client?.Dispose();
197 continue;
198 }
199 catch (Exception ex)
200 {
201 Client?.Dispose();
202 Log.Exception(ex);
203 continue;
204 }
205
206 ClusterUdpClient = new ClusterUdpClient(this, Client, Address);
207 ClusterUdpClient.BeginReceive();
208
209 this.incoming.AddLast(ClusterUdpClient);
210 this.endpoints = null;
211 }
212 }
213 }
214
215 try
216 {
217 Client = new UdpClient(Port, MulticastAddress.AddressFamily)
218 {
219 MulticastLoopback = true
220 };
221
222 if (IsMulticastAddress(MulticastAddress))
223 {
224 this.Information("Joining Multicast address " + MulticastAddress);
225
226 try
227 {
228 Client.JoinMulticastGroup(MulticastAddress);
229 }
230 catch (Exception ex)
231 {
232 Log.Exception(ex);
233 }
234 }
235 else
236 this.Warning("Address provided is not a multi-cast address.");
237
238 ClusterUdpClient = new ClusterUdpClient(this, Client, null);
239 ClusterUdpClient.BeginReceive();
240
241 this.incoming.AddLast(ClusterUdpClient);
242 this.endpoints = null;
243 }
244 catch (Exception)
245 {
246 // Ignore
247 }
248
249 this.aliveTimer = new Timer(this.SendAlive, null, 0, 5000);
250 }
251
255 public IPEndPoint[] Endpoints
256 {
257 get
258 {
259 if (this.endpoints is null)
260 {
261 List<IPEndPoint> Result = new List<IPEndPoint>();
262 LinkedListNode<ClusterUdpClient> Loop = this.incoming.First;
263
264 while (!(Loop is null))
265 {
266 Result.Add(Loop.Value.EndPoint);
267 Loop = Loop.Next;
268 }
269
270 this.endpoints = Result.ToArray();
271 }
272
273 return this.endpoints;
274 }
275 }
276
277 private IPEndPoint[] endpoints = null;
278
282 [Obsolete("Use DisposeAsync")]
283 public void Dispose()
284 {
285 this.DisposeAsync().Wait();
286 }
287
291 public async Task DisposeAsync()
292 {
293 if (!this.shuttingDown)
294 {
295 LinkedList<string> ToRelease = null;
296
297 lock (this.lockedResources)
298 {
299 foreach (LockInfo Info in this.lockedResources.Values)
300 {
301 if (Info.Locked)
302 {
303 ToRelease ??= new LinkedList<string>();
304 ToRelease.AddLast(Info.Resource);
305 }
306 }
307
308 this.lockedResources.Clear();
309 }
310
311 if (!(ToRelease is null))
312 {
313 foreach (string Resource in ToRelease)
314 {
315 await this.SendMessageAcknowledged(new Release()
316 {
317 Resource = Resource
318 }, null, null);
319 }
320 }
321
322 this.shuttingDown = true;
323 await this.SendMessageUnacknowledged(new ShuttingDown());
324
325 this.shutDown?.WaitOne(2000);
326 this.shutDown?.Dispose();
327 this.shutDown = null;
328
329 this.aes?.Dispose();
330 this.aes = null;
331 }
332 }
333
334 internal async Task Dispose2()
335 {
336 if (this.internalScheduler)
337 this.scheduler?.Dispose();
338
339 this.scheduler = null;
340
341 this.aliveTimer?.Dispose();
342 this.aliveTimer = null;
343
344 this.Clear(this.outgoing);
345 this.Clear(this.incoming);
346
347 this.currentStatus?.Dispose();
348 this.currentStatus = null;
349
350 foreach (ISniffer Sniffer in this.Sniffers)
351 {
352 try
353 {
354 if (Sniffer is IDisposableAsync DisposableAsync)
355 await DisposableAsync.DisposeAsync();
356 else if (Sniffer is IDisposable Disposable)
357 Disposable.Dispose();
358 }
359 catch (Exception ex)
360 {
361 Log.Exception(ex);
362 }
363 }
364
365 this.shutDown?.Set();
366 }
367
368 private void Clear(LinkedList<ClusterUdpClient> Clients)
369 {
370 LinkedListNode<ClusterUdpClient> Loop = Clients.First;
371
372 while (!(Loop is null))
373 {
374 try
375 {
376 Loop.Value.Dispose();
377 }
378 catch (Exception)
379 {
380 // Ignore
381 }
382
383 Loop = Loop.Next;
384 }
385
386 Clients.Clear();
387 }
388
389 internal static ObjectInfo GetObjectInfo(Type T)
390 {
391 lock (objectInfo)
392 {
393 if (objectInfo.TryGetValue(T, out ObjectInfo Result))
394 return Result;
395 }
396
397 List<PropertyReference> Properties = new List<PropertyReference>();
398
399 foreach (PropertyInfo PI in T.GetRuntimeProperties())
400 {
401 if (PI.GetMethod is null || PI.SetMethod is null)
402 continue;
403
404 Type PT = PI.PropertyType;
405 IProperty Property = GetProperty(PT);
406
407 if (Property is null)
408 continue;
409
410 Properties.Add(new PropertyReference()
411 {
412 Name = PI.Name,
413 Info = PI,
415 });
416 }
417
418 return new ObjectInfo()
419 {
420 Type = T,
421 Properties = Properties.ToArray()
422 };
423 }
424
425 internal static IProperty GetProperty(Type PT)
426 {
427 if (PT.IsArray)
428 {
429 Type ElementType = PT.GetElementType();
430 return new ArrayProperty(PT, ElementType, GetProperty(ElementType));
431 }
432
433 TypeInfo PTI = PT.GetTypeInfo();
434
435 if (PTI.IsEnum)
436 return new EnumProperty(PT);
437 else if (PTI.IsGenericType && PT.GetGenericTypeDefinition() == typeof(Nullable<>))
438 {
439 Type ElementType = PT.GenericTypeArguments[0];
440 IProperty Element = GetProperty(ElementType);
441
442 return new NullableProperty(PT, ElementType, Element);
443 }
444 else if (propertyTypes.TryGetValue(PT, out IProperty Property))
445 return Property;
446 else if (!PTI.IsValueType)
447 return new ObjectProperty(PT, GetObjectInfo(PT));
448 else
449 return null;
450 }
451
452 private static void Init()
453 {
454 Dictionary<Type, IProperty> PropertyTypes = new Dictionary<Type, IProperty>();
455
456 foreach (Type T in Types.GetTypesImplementingInterface(typeof(IProperty)))
457 {
458 ConstructorInfo DefaultConstructor = Types.GetDefaultConstructor(T);
459 if (DefaultConstructor is null)
460 continue;
461
462 try
463 {
464 IProperty Property = (IProperty)DefaultConstructor.Invoke(Types.NoParameters);
465 Type PT = Property.PropertyType;
466 if (PT is null)
467 continue;
468
469 if (PropertyTypes.ContainsKey(PT))
470 {
471 Log.Error("Multiple classes available for property type " + PT.FullName + ".");
472 continue;
473 }
474
475 PropertyTypes[PT] = Property;
476 }
477 catch (Exception)
478 {
479 continue;
480 }
481 }
482
483 if (propertyTypes is null)
484 Types.OnInvalidated += (Sender, e) => Init();
485
486 propertyTypes = PropertyTypes;
487 }
488
494 public byte[] Serialize(object Object)
495 {
496 using Serializer Output = new Serializer();
497 rootObject.Serialize(Output, Object);
498 return Output.ToArray();
499 }
500
507 public object Deserialize(byte[] Data)
508 {
509 using Deserializer Input = new Deserializer(Data);
510 return rootObject.Deserialize(Input, typeof(object));
511 }
512
513 internal async void DataReceived(bool ConstantBuffer, byte[] Data, IPEndPoint From)
514 {
515 try
516 {
517 if (Data.Length == 0)
518 return;
519
520 this.ReceiveBinary(ConstantBuffer, Data);
521
522 using Deserializer Input = new Deserializer(Data);
523 byte Command = Input.ReadByte();
524
525 switch (Command)
526 {
527 case 0: // Unacknowledged message
528 object Object = rootObject.Deserialize(Input, typeof(object));
529
530 if (this.HasSniffers)
531 this.Information("Unacknowledged message received: " + Object.GetType().FullName);
532
533 if (Object is IClusterMessage Message)
534 {
535 await Message.MessageReceived(this, From);
536
537 await this.OnMessageReceived.Raise(this, new ClusterMessageEventArgs(Message));
538 }
539 else
540 this.Error("Non-message object received in message: " + Object?.GetType()?.FullName);
541 break;
542
543 case 1: // Acknowledged message
544 Guid Id = Input.ReadGuid();
545 string s = Id.ToString();
546 ulong Len = Input.ReadVarUInt64();
547 bool Skip = false;
548
549 while (Len > 0)
550 {
551 string Address = new IPAddress(Input.ReadBinary()).ToString();
552 int Port = Input.ReadUInt16();
553
554 if (this.IsEndpoint(this.outgoing, Address, Port) ||
555 this.IsEndpoint(this.incoming, Address, Port))
556 {
557 Skip = true;
558 break;
559 }
560
561 Len--;
562 }
563
564 if (Skip)
565 break;
566
567 if (!this.currentStatus.TryGetValue(s, out object Obj) ||
568 !(Obj is bool Ack))
569 {
570 Object = rootObject.Deserialize(Input, typeof(object));
571
572 if (this.HasSniffers)
573 this.Information("Acknowledged message received: " + Object.GetType().FullName);
574
575 if (!((Message = Object as IClusterMessage) is null))
576 {
577 try
578 {
579 Ack = await Message.MessageReceived(this, From);
580
581 await this.OnMessageReceived.Raise(this, new ClusterMessageEventArgs(Message));
582 }
583 catch (Exception ex)
584 {
585 this.Exception(ex);
586 Log.Exception(ex);
587 Ack = false;
588 }
589
590 this.currentStatus[s] = Ack;
591 }
592 else
593 {
594 this.Error("Non-message object received in message: " + Object?.GetType()?.FullName);
595 Ack = false;
596 }
597 }
598
599 using (Serializer Output = new Serializer())
600 {
601 Output.WriteByte(Ack ? (byte)2 : (byte)3);
602 Output.WriteGuid(Id);
603
604 if (this.HasSniffers)
605 this.Information("Sending " + (Ack ? "ACK" : "NACK") + " to " + From.ToString());
606
607 this.Transmit(true, Output.ToArray(), From);
608 }
609 break;
610
611 case 2: // ACK
612 case 3: // NACK
613 Id = Input.ReadGuid();
614 s = Id.ToString();
615
616 if (this.HasSniffers)
617 this.Information(Command == 2 ? "ACK received" : "NACK received");
618
619 if (this.currentStatus.TryGetValue(s, out Obj) &&
620 Obj is MessageStatus MessageStatus)
621 {
622 lock (MessageStatus.Acknowledged)
623 {
624 MessageStatus.Acknowledged[From] = (Command == 2);
625 }
626
627 EndpointStatus[] CurrentStatus = this.GetRemoteStatuses();
628
629 if (MessageStatus.IsComplete(CurrentStatus))
630 {
631 this.currentStatus.Remove(s);
632 this.scheduler.Remove(MessageStatus.Timeout);
633
634 await MessageStatus.Callback.Raise(this, new ClusterMessageAckEventArgs(
635 MessageStatus.Message, MessageStatus.GetResponses(CurrentStatus),
636 MessageStatus.State));
637 }
638 }
639 else if (this.HasSniffers)
640 this.Warning("Response discarded. No status found for endpoing " + s);
641 break;
642
643 case 4: // Command
644 Id = Input.ReadGuid();
645 s = Id.ToString();
646 Len = Input.ReadVarUInt64();
647 Skip = false;
648
649 while (Len > 0)
650 {
651 string Address = new IPAddress(Input.ReadBinary()).ToString();
652 int Port = Input.ReadUInt16();
653
654 if (this.IsEndpoint(this.outgoing, Address, Port) ||
655 this.IsEndpoint(this.incoming, Address, Port))
656 {
657 Skip = true;
658 break;
659 }
660
661 Len--;
662 }
663
664 if (Skip)
665 break;
666
667 if (!this.currentStatus.TryGetValue(s, out Obj))
668 {
669 Object = rootObject.Deserialize(Input, typeof(object));
670
671 if (this.HasSniffers)
672 this.Information("Command received: " + Object.GetType().FullName);
673
674 if (Object is IClusterCommand ClusterCommand)
675 {
676 try
677 {
678 Obj = await ClusterCommand.Execute(this, From);
679 this.currentStatus[s] = Obj;
680 }
681 catch (Exception ex)
682 {
683 Obj = ex;
684 }
685 }
686 else
687 Obj = new Exception("Non-command object received in command: " + Object?.GetType()?.FullName);
688 }
689
690 using (Serializer Output = new Serializer())
691 {
692 if (Obj is Exception ex)
693 {
694 ex = Log.UnnestException(ex);
695
696 Output.WriteByte(6);
697 Output.WriteGuid(Id);
698 Output.WriteString(ex.Message);
699 Output.WriteString(ex.GetType().FullName);
700 }
701 else
702 {
703 Output.WriteByte(5);
704 Output.WriteGuid(Id);
705 rootObject.Serialize(Output, Obj);
706 }
707
708 this.Transmit(true, Output.ToArray(), From);
709 }
710 break;
711
712 case 5: // Command Response
713 Id = Input.ReadGuid();
714 s = Id.ToString();
715
716 if (this.currentStatus.TryGetValue(s, out Obj) &&
717 Obj is CommandStatusBase CommandStatus)
718 {
719 Object = rootObject.Deserialize(Input, typeof(object));
720
721 if (this.HasSniffers)
722 this.Information("Command Response received: " + Object.GetType().FullName);
723
724 CommandStatus.AddResponse(From, Object);
725
726 EndpointStatus[] CurrentStatus = this.GetRemoteStatuses();
727
728 if (CommandStatus.IsComplete(CurrentStatus))
729 {
730 this.currentStatus.Remove(s);
731 this.scheduler.Remove(CommandStatus.Timeout);
732
733 await CommandStatus.RaiseResponseEvent(CurrentStatus);
734 }
735 }
736 break;
737
738 case 6: // Command Exception
739 Id = Input.ReadGuid();
740 s = Id.ToString();
741
742 if (this.currentStatus.TryGetValue(s, out Obj) &&
743 Obj is CommandStatusBase CommandStatus2)
744 {
745 string ExceptionMessage = Input.ReadString();
746 string ExceptionType = Input.ReadString();
747 Exception ex;
748
749 try
750 {
751 Type T = Types.GetType(ExceptionType);
752 if (T is null)
753 ex = new Exception(ExceptionMessage);
754 else
755 ex = (Exception)Activator.CreateInstance(T, ExceptionMessage);
756 }
757 catch (Exception)
758 {
759 ex = new Exception(ExceptionMessage);
760 }
761
762 if (this.HasSniffers)
763 this.Error(ex.Message);
764
765 CommandStatus2.AddError(From, ex);
766
767 EndpointStatus[] CurrentStatus = this.GetRemoteStatuses();
768
769 if (CommandStatus2.IsComplete(CurrentStatus))
770 {
771 this.currentStatus.Remove(s);
772 this.scheduler.Remove(CommandStatus2.Timeout);
773
774 await CommandStatus2.RaiseResponseEvent(CurrentStatus);
775 }
776 }
777 break;
778
779 default:
780 this.Error("Invalid command received.");
781 break;
782 }
783 }
784 catch (Exception ex)
785 {
786 this.Exception(ex);
787 Log.Exception(ex);
788 }
789 }
790
791 internal bool IsEcho(IPEndPoint Endpoint)
792 {
793 LinkedListNode<ClusterUdpClient> Loop = this.outgoing.First;
794
795 while (!(Loop is null))
796 {
797 if (Endpoint.Equals(Loop.Value.EndPoint))
798 return true;
799
800 Loop = Loop.Next;
801 }
802
803 return false;
804 }
805
806 private bool IsEndpoint(LinkedList<ClusterUdpClient> Clients, string Address, int Port)
807 {
808 LinkedListNode<ClusterUdpClient> Loop = Clients.First;
809
810 while (!(Loop is null))
811 {
812 if (Loop.Value.IsEndpoint(Address, Port))
813 return true;
814 }
815
816 return false;
817 }
818
822 public event EventHandlerAsync<ClusterMessageEventArgs> OnMessageReceived = null;
823
824 private void Transmit(bool ConstantBuffer, byte[] Message, IPEndPoint Destination)
825 {
826 this.TransmitBinary(ConstantBuffer, Message);
827
828 LinkedListNode<ClusterUdpClient> Loop = this.outgoing.First;
829
830 while (!(Loop is null))
831 {
832 Loop.Value.BeginTransmit(ConstantBuffer, Message, Destination);
833 Loop = Loop.Next;
834 }
835 }
836
842 {
843 return this.SendMessageUnacknowledged(Message, this.destination);
844 }
845
851 public async Task SendMessageUnacknowledged(IClusterMessage Message, IPEndPoint Destination)
852 {
853 if (this.HasSniffers)
854 this.Information("Sending unacknowledged message: " + Message.GetType().FullName);
855
856 Serializer Output = new Serializer();
857
858 try
859 {
860 Output.WriteByte(0); // Unacknowledged message.
861 rootObject.Serialize(Output, Message);
862 this.Transmit(true, Output.ToArray(), Destination);
863 }
864 catch (Exception ex)
865 {
866 Log.Exception(ex);
867
868 if (this.shuttingDown)
869 await this.Dispose2();
870 }
871 finally
872 {
873 Output.Dispose();
874 }
875 }
876
884 EventHandlerAsync<ClusterMessageAckEventArgs> Callback, object State)
885 {
886 return this.SendMessageAcknowledged(Message, this.destination, Callback, State);
887 }
888
896 public async Task SendMessageAcknowledged(IClusterMessage Message, IPEndPoint Destination,
897 EventHandlerAsync<ClusterMessageAckEventArgs> Callback, object State)
898 {
899 if (this.HasSniffers)
900 this.Information("Sending acknowledged message: " + Message.GetType().FullName);
901
902 Serializer Output = new Serializer();
903 Guid Id = Guid.NewGuid();
904 string s = Id.ToString();
905 MessageStatus Rec = null;
906
907 try
908 {
909 Output.WriteByte(1); // Acknowledged message.
910 Output.WriteGuid(Id);
911 Output.WriteVarUInt64(0); // Endpoints to skip
912 rootObject.Serialize(Output, Message);
913
914 byte[] Bin = Output.ToArray();
915 DateTime Now = DateTime.Now;
916
917 Rec = new MessageStatus()
918 {
919 Id = Id,
920 Message = Message,
921 MessageBinary = Bin,
922 Destination = Destination,
923 Callback = Callback,
924 State = State,
925 TimeLimit = Now.AddSeconds(30)
926 };
927
928 this.currentStatus[s] = Rec;
929
930 Rec.Timeout = this.scheduler.Add(Now.AddSeconds(2), this.ResendAcknowledgedMessage, Rec);
931
932 this.Transmit(true, Bin, Destination);
933
934 EndpointStatus[] CurrentStatus = this.GetRemoteStatuses();
935
936 if (Rec.IsComplete(CurrentStatus))
937 {
938 this.currentStatus.Remove(s);
939 this.scheduler.Remove(Rec.Timeout);
940
941 await Rec.Callback.Raise(this, new ClusterMessageAckEventArgs(
942 Rec.Message, Rec.GetResponses(CurrentStatus), Rec.State));
943 }
944 }
945 catch (Exception ex)
946 {
947 Log.Exception(ex);
948
949 if (this.shuttingDown)
950 await this.Dispose2();
951 }
952 finally
953 {
954 Output.Dispose();
955 }
956 }
957
958 private async Task ResendAcknowledgedMessage(object P)
959 {
960 MessageStatus Rec = (MessageStatus)P;
961
962 try
963 {
964 EndpointStatus[] CurrentStatus = this.GetRemoteStatuses();
965 DateTime Now = DateTime.Now;
966
967 if (Rec.IsComplete(CurrentStatus) || Now >= Rec.TimeLimit)
968 {
969 this.currentStatus.Remove(Rec.Id.ToString());
970 this.scheduler.Remove(Rec.Timeout);
971
972 await Rec.Callback.Raise(this, new ClusterMessageAckEventArgs(
973 Rec.Message, Rec.GetResponses(CurrentStatus), Rec.State));
974 }
975 else
976 {
977 using Serializer Output = new Serializer();
978 Output.WriteByte(1); // Acknowledged message.
979 Output.WriteGuid(Rec.Id);
980
981 lock (Rec.Acknowledged)
982 {
983 Output.WriteVarUInt64((uint)Rec.Acknowledged.Count); // Endpoints to skip
984
985 foreach (IPEndPoint Endpoint in Rec.Acknowledged.Keys)
986 {
987 Output.WriteBinary(Endpoint.Address.GetAddressBytes());
988 Output.WriteUInt16((ushort)Endpoint.Port);
989 }
990 }
991
992 Output.WriteRaw(Rec.MessageBinary, 18, Rec.MessageBinary.Length - 18);
993
994 Rec.Timeout = this.scheduler.Add(Now.AddSeconds(2), this.ResendAcknowledgedMessage, Rec);
995 this.Transmit(true, Output.ToArray(), Rec.Destination);
996 }
997 }
998 catch (Exception ex)
999 {
1000 Log.Exception(ex);
1001 }
1002 }
1003
1008 public Task<EndpointAcknowledgement[]> SendMessageAcknowledgedAsync(IClusterMessage Message)
1009 {
1010 return this.SendMessageAcknowledgedAsync(Message, this.destination);
1011 }
1012
1018 public async Task<EndpointAcknowledgement[]> SendMessageAcknowledgedAsync(IClusterMessage Message, IPEndPoint Destination)
1019 {
1020 TaskCompletionSource<EndpointAcknowledgement[]> Result = new TaskCompletionSource<EndpointAcknowledgement[]>();
1021
1022 await this.SendMessageAcknowledged(Message, Destination, (Sender, e) =>
1023 {
1024 Result.TrySetResult(e.Responses);
1025 return Task.CompletedTask;
1026 }, null);
1027
1028 return await Result.Task;
1029 }
1030
1038 EventHandlerAsync<ClusterMessageAckEventArgs> Callback, object State)
1039 {
1040 return this.SendMessageAssured(Message, this.destination, Callback, State);
1041 }
1042
1050 public async Task SendMessageAssured(IClusterMessage Message, IPEndPoint Destination,
1051 EventHandlerAsync<ClusterMessageAckEventArgs> Callback, object State)
1052 {
1053 Guid MessageId = Guid.NewGuid();
1054
1055 await this.SendMessageAcknowledged(new Transport()
1056 {
1057 MessageID = MessageId,
1058 Message = Message
1059 }, Destination, async (Sender, e) =>
1060 {
1061 await this.SendMessageAcknowledged(new Deliver()
1062 {
1063 MessageID = MessageId
1064 }, Destination, async (sender2, e2) =>
1065 {
1066 await Callback.Raise(this, new ClusterMessageAckEventArgs(Message, e2.Responses, State));
1067 }, null);
1068 }, null);
1069 }
1070
1075 public Task<EndpointAcknowledgement[]> SendMessageAssuredAsync(IClusterMessage Message)
1076 {
1077 return this.SendMessageAssuredAsync(Message, this.destination);
1078 }
1079
1085 public async Task<EndpointAcknowledgement[]> SendMessageAssuredAsync(IClusterMessage Message, IPEndPoint Destination)
1086 {
1087 TaskCompletionSource<EndpointAcknowledgement[]> Result = new TaskCompletionSource<EndpointAcknowledgement[]>();
1088
1089 await this.SendMessageAssured(Message, Destination, (Sender, e) =>
1090 {
1091 Result.TrySetResult(e.Responses);
1092 return Task.CompletedTask;
1093 }, null);
1094
1095 return await Result.Task;
1096 }
1097
1098 private void SendAlive(object _)
1099 {
1100 Task.Run(() => this.SendAlive(this.destination));
1101 }
1102
1103 private async Task SendAlive(IPEndPoint Destination)
1104 {
1105 try
1106 {
1108
1109 await this.GetStatus.Raise(this, e);
1110
1111 this.localStatus = e.Status;
1112
1113 await this.SendMessageUnacknowledged(new Alive()
1114 {
1115 Status = this.localStatus
1116 }, Destination);
1117 }
1118 catch (Exception ex)
1119 {
1120 Log.Exception(ex);
1121 }
1122 }
1123
1127 public event EventHandlerAsync<ClusterGetStatusEventArgs> GetStatus = null;
1128
1132 public object LocalStatus => this.localStatus;
1133
1140 {
1141 EndpointStatus[] Result;
1142 int i, c;
1143
1144 lock (this.remoteStatus)
1145 {
1146 Result = new EndpointStatus[c = this.remoteStatus.Count];
1147
1148 i = 0;
1149 foreach (KeyValuePair<IPEndPoint, object> P in this.remoteStatus)
1150 Result[i++] = new EndpointStatus(P.Key, P.Value);
1151 }
1152
1153 return Result;
1154 }
1155
1163 public async void AddRemoteStatus(IPEndPoint Endpoint, object Status)
1164 {
1165 try
1166 {
1167 bool New;
1168
1169 lock (this.remoteStatus)
1170 {
1171 New = !this.remoteStatus.ContainsKey(Endpoint);
1172 this.remoteStatus[Endpoint] = Status;
1173 }
1174
1175 if (New)
1176 {
1177 await this.SendAlive(Endpoint);
1178
1179 if (this.HasSniffers)
1180 this.Information("New remote endpoint: " + Endpoint.ToString());
1181
1182 await this.EndpointOnline.Raise(this, new ClusterEndpointEventArgs(Endpoint));
1183 }
1184 else
1185 this.Information("Remote endpoint status updated: " + Endpoint.ToString());
1186
1187 await this.EndpointStatus.Raise(this, new ClusterEndpointStatusEventArgs(Endpoint, Status));
1188 }
1189 catch (Exception ex)
1190 {
1191 Log.Exception(ex);
1192 this.Exception(ex);
1193 }
1194 }
1195
1201 public async Task<bool> RemoveRemoteStatus(IPEndPoint Endpoint)
1202 {
1203 bool Removed;
1204
1205 lock (this.remoteStatus)
1206 {
1207 Removed = this.remoteStatus.Remove(Endpoint);
1208 }
1209
1210 if (Removed)
1211 {
1212 if (this.HasSniffers)
1213 this.Warning("Remote endpoint removed: " + Endpoint.ToString());
1214
1215 await this.EndpointOffline.Raise(this, new ClusterEndpointEventArgs(Endpoint));
1216 }
1217
1218 return Removed;
1219 }
1220
1224 public event EventHandlerAsync<ClusterEndpointEventArgs> EndpointOnline = null;
1225
1229 public event EventHandlerAsync<ClusterEndpointEventArgs> EndpointOffline = null;
1230
1234 public event EventHandlerAsync<ClusterEndpointStatusEventArgs> EndpointStatus = null;
1235
1236 internal void StatusReported(object Status, IPEndPoint RemoteEndpoint)
1237 {
1238 this.AddRemoteStatus(RemoteEndpoint, Status);
1239
1240 string s = RemoteEndpoint.ToString();
1241
1242 if (!this.currentStatus.ContainsKey(s))
1243 this.currentStatus[s] = RemoteEndpoint;
1244 }
1245
1246 internal Task EndpointShutDown(IPEndPoint RemoteEndpoint)
1247 {
1248 return this.RemoveRemoteStatus(RemoteEndpoint);
1249 }
1250
1251 internal void AssuredTransport(Guid MessageId, IClusterMessage Message)
1252 {
1253 this.currentStatus[MessageId.ToString()] = Message;
1254 }
1255
1256 internal async Task<bool> AssuredDelivery(Guid MessageId, ClusterEndpoint Endpoint, IPEndPoint RemoteEndpoint)
1257 {
1258 string s = MessageId.ToString();
1259
1260 if (this.currentStatus.TryGetValue(s, out object Obj))
1261 {
1262 if (Obj is bool b)
1263 return b;
1264 else if (Obj is IClusterMessage Message)
1265 {
1266 this.currentStatus[s] = false;
1267 b = await Message.MessageReceived(Endpoint, RemoteEndpoint);
1268 this.currentStatus[s] = b;
1269
1270 await this.OnMessageReceived.Raise(this, new ClusterMessageEventArgs(Message));
1271
1272 return b;
1273 }
1274 else
1275 return false;
1276 }
1277 else
1278 return false;
1279 }
1280
1281 private async Task CurrentStatus_Removed(object Sender, CacheItemEventArgs<string, object> e)
1282 {
1283 if (e.Value is IPEndPoint RemoteEndpoint)
1284 {
1285 if (this.HasSniffers)
1286 this.Warning("Remote endpoint dropped: " + RemoteEndpoint.ToString());
1287
1288 await this.RemoveRemoteStatus(RemoteEndpoint);
1289 }
1290 else if (e.Value is MessageStatus MessageStatus)
1291 {
1292 if (e.Reason != RemovedReason.Manual)
1293 {
1294 this.scheduler.Remove(MessageStatus.Timeout);
1295
1296 if (this.HasSniffers)
1297 this.Warning("Timeout: " + MessageStatus.Message.GetType().FullName);
1298
1299 await MessageStatus.Callback.Raise(this, new ClusterMessageAckEventArgs(
1300 MessageStatus.Message, MessageStatus.GetResponses(this.GetRemoteStatuses()),
1301 MessageStatus.State));
1302 }
1303 }
1304 }
1305
1311 public Task Ping(EventHandlerAsync<ClusterMessageAckEventArgs> Callback, object State)
1312 {
1313 return this.SendMessageAcknowledged(new Messages.Ping(), Callback, State);
1314 }
1315
1319 public async Task<EndpointAcknowledgement[]> PingAsync()
1320 {
1321 TaskCompletionSource<EndpointAcknowledgement[]> Result = new TaskCompletionSource<EndpointAcknowledgement[]>();
1322
1323 await this.Ping((Sender, e) =>
1324 {
1325 Result.TrySetResult(e.Responses);
1326 return Task.CompletedTask;
1327 }, null);
1328
1329 return await Result.Task;
1330 }
1331
1343 {
1344 Serializer Output = new Serializer();
1345 Guid Id = Guid.NewGuid();
1346 string s = Id.ToString();
1347 CommandStatus<ResponseType> Rec = null;
1348
1349 try
1350 {
1351 Output.WriteByte(4); // Command.
1352 Output.WriteGuid(Id);
1353 Output.WriteVarUInt64(0); // Endpoints to skip
1354 rootObject.Serialize(Output, Command);
1355
1356 byte[] Bin = Output.ToArray();
1357 DateTime Now = DateTime.Now;
1358
1359 Rec = new CommandStatus<ResponseType>()
1360 {
1361 Id = Id,
1362 IdStr = s,
1363 Command = Command,
1364 CommandBinary = Bin,
1365 TimeLimit = Now.AddSeconds(30),
1366 Callback = Callback,
1367 State = State
1368 };
1369
1370 this.currentStatus[s] = Rec;
1371
1372 Rec.Timeout = this.scheduler.Add(Now.AddSeconds(2), this.ResendCommand<ResponseType>, Rec);
1373
1374 this.Transmit(true, Bin, this.destination);
1375
1376 EndpointStatus[] CurrentStatus = this.GetRemoteStatuses();
1377
1378 if (Rec.IsComplete(CurrentStatus))
1379 {
1380 this.currentStatus.Remove(Rec.IdStr);
1381 this.scheduler.Remove(Rec.Timeout);
1382
1383 await Rec.Callback.Raise(this, new ClusterResponseEventArgs<ResponseType>(
1384 Rec.Command, Rec.GetResponses(CurrentStatus), Rec.State));
1385 }
1386 }
1387 catch (Exception ex)
1388 {
1389 Log.Exception(ex);
1390
1391 if (this.shuttingDown)
1392 await this.Dispose2();
1393 }
1394 finally
1395 {
1396 Output.Dispose();
1397 }
1398 }
1399
1400 private async Task ResendCommand<ResponseType>(object P)
1401 {
1402 CommandStatus<ResponseType> Rec = (CommandStatus<ResponseType>)P;
1403 DateTime Now = DateTime.Now;
1404
1405 try
1406 {
1407 EndpointStatus[] CurrentStatus = this.GetRemoteStatuses();
1408
1409 if (Rec.IsComplete(CurrentStatus) || Now >= Rec.TimeLimit)
1410 {
1411 this.currentStatus.Remove(Rec.IdStr);
1412 this.scheduler.Remove(Rec.Timeout);
1413
1414 await Rec.Callback.Raise(this, new ClusterResponseEventArgs<ResponseType>(
1415 Rec.Command, Rec.GetResponses(CurrentStatus), Rec.State));
1416 }
1417 else
1418 {
1419 using Serializer Output = new Serializer();
1420
1421 Output.WriteByte(4); // Command.
1422 Output.WriteGuid(Rec.Id);
1423
1424 lock (Rec.Responses)
1425 {
1426 Output.WriteVarUInt64((uint)Rec.Responses.Count); // Endpoints to skip
1427
1428 foreach (IPEndPoint Endpoint in Rec.Responses.Keys)
1429 {
1430 Output.WriteBinary(Endpoint.Address.GetAddressBytes());
1431 Output.WriteUInt16((ushort)Endpoint.Port);
1432 }
1433 }
1434
1435 Output.WriteRaw(Rec.CommandBinary, 18, Rec.CommandBinary.Length - 18);
1436
1437 Rec.Timeout = this.scheduler.Add(Now.AddSeconds(2), this.ResendCommand<ResponseType>, Rec);
1438 this.Transmit(true, Output.ToArray(), this.destination);
1439 }
1440 }
1441 catch (Exception ex)
1442 {
1443 Log.Exception(ex);
1444 }
1445 }
1446
1455 public async Task<EndpointResponse<ResponseType>[]> ExecuteCommandAsync<ResponseType>(IClusterCommand Command)
1456 {
1457 TaskCompletionSource<EndpointResponse<ResponseType>[]> Result =
1458 new TaskCompletionSource<EndpointResponse<ResponseType>[]>();
1459
1460 await this.ExecuteCommand<ResponseType>(Command, (Sender, e) =>
1461 {
1462 Result.TrySetResult(e.Responses);
1463 return Task.CompletedTask;
1464 }, null);
1465
1466 return await Result.Task;
1467 }
1468
1475 public Task Echo(string Text, EventHandlerAsync<ClusterResponseEventArgs<string>> Callback, object State)
1476 {
1477 return this.ExecuteCommand(new Echo()
1478 {
1479 Text = Text
1480 }, Callback, State);
1481 }
1482
1487 public async Task<EndpointResponse<string>[]> EchoAsync(string Text)
1488 {
1489 TaskCompletionSource<EndpointResponse<string>[]> Result = new TaskCompletionSource<EndpointResponse<string>[]>();
1490
1491 await this.Echo(Text, (Sender, e) =>
1492 {
1493 Result.TrySetResult(e.Responses);
1494 return Task.CompletedTask;
1495 }, null);
1496
1497 return await Result.Task;
1498 }
1499
1505 public Task GetAssemblies(EventHandlerAsync<ClusterResponseEventArgs<string[]>> Callback, object State)
1506 {
1507 return this.ExecuteCommand(new Assemblies(), Callback, State);
1508 }
1509
1513 public async Task<EndpointResponse<string[]>[]> GetAssembliesAsync()
1514 {
1515 TaskCompletionSource<EndpointResponse<string[]>[]> Result = new TaskCompletionSource<EndpointResponse<string[]>[]>();
1516
1517 await this.GetAssemblies((Sender, e) =>
1518 {
1519 Result.TrySetResult(e.Responses);
1520 return Task.CompletedTask;
1521 }, null);
1522
1523 return await Result.Task;
1524 }
1525
1533 public Task Lock(string ResourceName, int TimeoutMilliseconds,
1534 EventHandlerAsync<ClusterResourceLockEventArgs> Callback, object State)
1535 {
1536 LockInfo Info;
1537 LockInfoRec InfoRec;
1538
1539 lock (this.lockedResources)
1540 {
1541 if (!this.lockedResources.TryGetValue(ResourceName, out Info))
1542 {
1543 Info = new LockInfo()
1544 {
1545 Resource = ResourceName,
1546 Locked = false
1547 };
1548
1549 this.lockedResources[ResourceName] = Info;
1550 }
1551
1552 Info.Queue.AddLast(InfoRec = new LockInfoRec()
1553 {
1554 Info = Info,
1555 Timeout = DateTime.Now.AddMilliseconds(TimeoutMilliseconds),
1556 Callback = Callback,
1557 State = State
1558 });
1559 }
1560
1561 if (Info.Locked)
1562 return this.LockResult(false, null, InfoRec);
1563 else
1564 return this.Lock(Info, InfoRec);
1565 }
1566
1567 private Task Lock(LockInfo Info, LockInfoRec InfoRec)
1568 {
1569 return this.SendMessageAcknowledged(new Lock()
1570 {
1571 Resource = Info.Resource
1572 }, (Sender, e) =>
1573 {
1574 IPEndPoint LockedBy = null;
1575
1576 foreach (EndpointAcknowledgement Response in e.Responses)
1577 {
1578 if (Response.ACK.HasValue && !Response.ACK.Value)
1579 {
1580 LockedBy = Response.Endpoint;
1581 break;
1582 }
1583 }
1584
1585 bool Ok;
1586
1587 lock (this.lockedResources)
1588 {
1589 if (Info.Locked)
1590 Ok = false;
1591 else
1592 Ok = LockedBy is null;
1593
1594 if (Ok)
1595 {
1596 Info.Locked = true;
1597 Info.Queue.Remove(InfoRec);
1598 }
1599 else if (InfoRec.Timeout <= DateTime.Now)
1600 {
1601 Info.Queue.Remove(InfoRec);
1602
1603 if (!Info.Locked && Info.Queue.First is null)
1604 this.lockedResources.Remove(Info.Resource);
1605 }
1606 }
1607
1608 return this.LockResult(Ok, LockedBy, InfoRec);
1609
1610 }, null);
1611 }
1612
1613 private async Task LockResult(bool Ok, IPEndPoint LockedBy, LockInfoRec InfoRec)
1614 {
1615 if (Ok)
1616 await this.Raise(InfoRec.Info.Resource, true, null, InfoRec);
1617 else if (DateTime.Now >= InfoRec.Timeout)
1618 await this.Raise(InfoRec.Info.Resource, false, LockedBy, InfoRec);
1619 else if (!InfoRec.TimeoutScheduled)
1620 {
1621 InfoRec.LockedBy = LockedBy;
1622 InfoRec.Timeout = this.scheduler.Add(InfoRec.Timeout, this.LockTimeout, InfoRec);
1623 InfoRec.TimeoutScheduled = true;
1624 }
1625 }
1626
1627 private Task LockTimeout(object P)
1628 {
1629 LockInfoRec InfoRec = (LockInfoRec)P;
1630 LockInfo Info = InfoRec.Info;
1631
1632 InfoRec.TimeoutScheduled = false;
1633
1634 lock (this.lockedResources)
1635 {
1636 Info.Queue.Remove(InfoRec);
1637
1638 if (!Info.Locked && Info.Queue.First is null)
1639 this.lockedResources.Remove(Info.Resource);
1640 }
1641
1642 return this.Raise(Info.Resource, false, InfoRec.LockedBy, InfoRec);
1643 }
1644
1645 private async Task Raise(string ResourceName, bool LockSuccessful, IPEndPoint LockedBy,
1646 LockInfoRec InfoRec)
1647 {
1648 try
1649 {
1650 if (InfoRec.TimeoutScheduled)
1651 {
1652 this.scheduler.Remove(InfoRec.Timeout);
1653 InfoRec.TimeoutScheduled = false;
1654 }
1655
1656 LockInfo Info = InfoRec.Info;
1657
1658 lock (this.lockedResources)
1659 {
1660 Info.Queue.Remove(InfoRec);
1661
1662 if (!Info.Locked && Info.Queue.First is null)
1663 this.lockedResources.Remove(Info.Resource);
1664 }
1665
1666 await InfoRec.Callback.Raise(this, new ClusterResourceLockEventArgs(ResourceName,
1667 LockSuccessful, LockedBy, InfoRec.State));
1668 }
1669 catch (Exception ex)
1670 {
1671 Log.Exception(ex);
1672 }
1673 }
1674
1680 public async Task<ClusterResourceLockEventArgs> LockAsync(string ResourceName, int TimeoutMilliseconds)
1681 {
1682 TaskCompletionSource<ClusterResourceLockEventArgs> Result = new TaskCompletionSource<ClusterResourceLockEventArgs>();
1683
1684 await this.Lock(ResourceName, TimeoutMilliseconds, (Sender, e) =>
1685 {
1686 Result.TrySetResult(e);
1687 return Task.CompletedTask;
1688 }, null);
1689
1690 return await Result.Task;
1691 }
1692
1698 public async Task Release(string ResourceName)
1699 {
1700 LockInfo Info;
1701 LockInfoRec InfoRec;
1702
1703 lock (this.lockedResources)
1704 {
1705 if (!this.lockedResources.TryGetValue(ResourceName, out Info) || !Info.Locked)
1706 throw new ArgumentException("Resource not locked.", nameof(ResourceName));
1707
1708 Info.Locked = false;
1709
1710 if (Info.Queue.First is null)
1711 {
1712 this.lockedResources.Remove(ResourceName);
1713 InfoRec = null;
1714 }
1715 else
1716 InfoRec = Info.Queue.First.Value;
1717 }
1718
1719 await this.SendMessageAcknowledged(new Release()
1720 {
1721 Resource = ResourceName
1722 }, null, null);
1723
1724 if (!(InfoRec is null))
1725 {
1726 this.scheduler.Add(DateTime.Now.AddMilliseconds(this.rnd.Next(50) + 1), (P) =>
1727 {
1728 this.Lock(Info, InfoRec);
1729 }, null);
1730 }
1731 }
1732
1733 internal void Released(string ResourceName)
1734 {
1735 LockInfo Info;
1736 LockInfoRec InfoRec;
1737
1738 lock (this.lockedResources)
1739 {
1740 if (!this.lockedResources.TryGetValue(ResourceName, out Info) || Info.Locked)
1741 return;
1742
1743 if (Info.Queue.First is null)
1744 {
1745 this.lockedResources.Remove(ResourceName);
1746 return;
1747 }
1748 else
1749 InfoRec = Info.Queue.First.Value;
1750 }
1751
1752 this.scheduler.Add(DateTime.Now.AddMilliseconds(this.rnd.Next(50) + 1), (P) =>
1753 {
1754 this.Lock(Info, InfoRec);
1755 }, null);
1756 }
1757
1758 }
1759}
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
static void Error(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an error event.
Definition: Log.cs:692
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Definition: Log.cs:828
Event arguments for cluster endpoint events.
Represents one endpoint (or participant) in the network cluster.
Task SendMessageAcknowledged(IClusterMessage Message, EventHandlerAsync< ClusterMessageAckEventArgs > Callback, object State)
Sends a message using acknowledged service. ("at least once")
async Task< EndpointAcknowledgement[]> SendMessageAcknowledgedAsync(IClusterMessage Message, IPEndPoint Destination)
Sends a message using acknowledged service.
Task Lock(string ResourceName, int TimeoutMilliseconds, EventHandlerAsync< ClusterResourceLockEventArgs > Callback, object State)
Locks a singleton resource in the cluster.
Task Echo(string Text, EventHandlerAsync< ClusterResponseEventArgs< string > > Callback, object State)
Asks endpoints in the cluster to echo a text string back to the sender.
async Task ExecuteCommand< ResponseType >(IClusterCommand Command, EventHandlerAsync< ClusterResponseEventArgs< ResponseType > > Callback, object State)
Execute a command on the other members of the cluster, and waits for responses to be returned....
async Task< EndpointAcknowledgement[]> SendMessageAssuredAsync(IClusterMessage Message, IPEndPoint Destination)
Sends a message using assured service. ("exactly once")
Task< EndpointAcknowledgement[]> SendMessageAssuredAsync(IClusterMessage Message)
Sends a message using assured service. ("exactly once")
async Task< ClusterResourceLockEventArgs > LockAsync(string ResourceName, int TimeoutMilliseconds)
Locks a singleton resource in the cluster.
ClusterEndpoint(IPAddress MulticastAddress, int Port, string SharedSecret, params ISniffer[] Sniffers)
Represents one endpoint (or participant) in the network cluster.
EventHandlerAsync< ClusterGetStatusEventArgs > GetStatus
Event raised when current status is needed.
Task< EndpointAcknowledgement[]> SendMessageAcknowledgedAsync(IClusterMessage Message)
Sends a message using acknowledged service.
async Task< bool > RemoveRemoteStatus(IPEndPoint Endpoint)
Explicitly removes a remote cluster endpoint status object.
async Task DisposeAsync()
IDisposable.Dispose
Task SendMessageUnacknowledged(IClusterMessage Message)
Sends an unacknowledged message ("at most once")
object LocalStatus
Local status. For remote endpoint statuses, see GetRemoteStatuses
async Task Release(string ResourceName)
Releases a resource.
EndpointStatus[] GetRemoteStatuses()
Gets current remote statuses. For the current local status, see LocalStatus
async Task SendMessageAcknowledged(IClusterMessage Message, IPEndPoint Destination, EventHandlerAsync< ClusterMessageAckEventArgs > Callback, object State)
Sends a message using acknowledged service. ("at least once")
IPEndPoint[] Endpoints
IP endpoints listening on.
EventHandlerAsync< ClusterEndpointEventArgs > EndpointOffline
Event raised when an endpoint goes offline.
object Deserialize(byte[] Data)
Deserializes an object.
async Task SendMessageUnacknowledged(IClusterMessage Message, IPEndPoint Destination)
Sends an unacknowledged message ("at most once")
async Task SendMessageAssured(IClusterMessage Message, IPEndPoint Destination, EventHandlerAsync< ClusterMessageAckEventArgs > Callback, object State)
Sends a message using assured service. ("exactly once")
EventHandlerAsync< ClusterEndpointStatusEventArgs > EndpointStatus
Event raised when status has been reported by an endpoint.
EventHandlerAsync< ClusterEndpointEventArgs > EndpointOnline
Event raised when a new endpoint is available in the cluster.
byte[] Serialize(object Object)
Serializes an object.
Task GetAssemblies(EventHandlerAsync< ClusterResponseEventArgs< string[]> > Callback, object State)
Asks endpoints in the cluster to return assemblies available in their runtime environment.
async void AddRemoteStatus(IPEndPoint Endpoint, object Status)
Explicitly adds a remote endpoint status object. In normal operation, calling this method is not nece...
async Task< EndpointResponse< string >[]> EchoAsync(string Text)
Asks endpoints in the cluster to echo a text string back to the sender.
EventHandlerAsync< ClusterMessageEventArgs > OnMessageReceived
Event raised when a cluster message has been received.
async Task< EndpointResponse< string[]>[]> GetAssembliesAsync()
Asks endpoints in the cluster to return assemblies available in their runtime environment.
Task SendMessageAssured(IClusterMessage Message, EventHandlerAsync< ClusterMessageAckEventArgs > Callback, object State)
Sends a message using assured service. ("exactly once")
ClusterEndpoint(IPAddress MulticastAddress, int Port, byte[] SharedSecret, params ISniffer[] Sniffers)
Represents one endpoint (or participant) in the network cluster.
Task Ping(EventHandlerAsync< ClusterMessageAckEventArgs > Callback, object State)
Sends an acknowledged ping message to the other servers in the cluster.
async Task< EndpointResponse< ResponseType >[]> ExecuteCommandAsync< ResponseType >(IClusterCommand Command)
Execute a command on the other members of the cluster, and waits for responses to be returned....
async Task< EndpointAcknowledgement[]> PingAsync()
Sends an acknowledged ping message to the other servers in the cluster.
Event arguments for cluster endpoint status events.
Event arguments for cluster get status events.
Event arguments for cluster message acknowledgement events.
Event arguments for cluster message events.
Event arguments for cluster resource lock events.
Event arguments for cluster message acknowledgement events.
Command to request available assembly names from cluster endpoints.
Definition: Assemblies.cs:14
Command echoing incoming text string.
Definition: Echo.cs:13
Contains information about one of the endpoints in the cluster.
Contains information about one of the endpoints in the cluster.
Alive message regularly sent on the network to inform members of the cluster of the status of the cur...
Definition: Alive.cs:12
Delivers a message, as part of an assured message transfer
Definition: Deliver.cs:11
Message used to inform other members that the sending endpoint is trying to lock a resource....
Definition: Lock.cs:14
Ping message that can be sent to test endpoints in cluster receive messages.
Definition: Ping.cs:11
Message used to inform other members that the sending endpoint is releasing a locked resource.
Definition: Release.cs:13
Message sent when an endpoint is shut down, to let the other endpoints know the current endpoint is n...
Definition: ShuttingDown.cs:12
Transports a message, as part of an assured message transfer
Definition: Transport.cs:11
void WriteRaw(byte[] Binary)
Writes raw binary data to the output.
Definition: Serializer.cs:98
byte[] ToArray()
Returns the binary output.
Definition: Serializer.cs:36
void WriteVarUInt64(ulong Value)
Writes a variable-length unsigned integer to the output.
Definition: Serializer.cs:63
void WriteBinary(byte[] Value)
Writes binary data to the output.
Definition: Serializer.cs:83
void WriteGuid(Guid Value)
Writes a Guid to the output.
Definition: Serializer.cs:320
void WriteString(string Value)
Writes a string to the output.
Definition: Serializer.cs:118
void WriteByte(byte Value)
Writes a byte to the output.
Definition: Serializer.cs:54
void WriteUInt16(ushort Value)
Writes a 16-bit unsigned integer to the output.
Definition: Serializer.cs:202
Simple base class for classes implementing communication protocols.
static bool IsMulticastAddress(IPAddress Address)
Checks if an IP Address is a multi-cast address or not.
void Exception(Exception Exception)
Called to inform the viewer of an exception state.
ISniffer[] Sniffers
Registered sniffers.
bool HasSniffers
If there are sniffers registered on the object.
void Error(string Error)
Called to inform the viewer of an error state.
void Warning(string Warning)
Called to inform the viewer of a warning state.
void TransmitBinary(int Count)
Called when binary data has been transmitted.
void ReceiveBinary(int Count)
Called when binary data has been received.
void Information(string Comment)
Called to inform the viewer of something.
Implements an in-memory cache.
Definition: Cache.cs:17
bool ContainsKey(KeyType Key)
Checks if a key is available in the cache.
Definition: Cache.cs:404
void Dispose()
IDisposable.Dispose
Definition: Cache.cs:99
bool Remove(KeyType Key)
Removes an item from the cache.
Definition: Cache.cs:616
bool TryGetValue(KeyType Key, out ValueType Value)
Tries to get a value from the cache.
Definition: Cache.cs:311
Event arguments for cache item removal events.
ValueType Value
Value of item that was removed.
RemovedReason Reason
Reason for removing the item.
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:15
static Type GetType(string FullName)
Gets a type, given its full name.
Definition: Types.cs:42
static bool TryGetModuleParameter(string Name, out object Value)
Tries to get a module parameter value.
Definition: Types.cs:607
static object[] NoParameters
Contains an empty array of parameter values.
Definition: Types.cs:572
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
Definition: Types.cs:85
static ConstructorInfo GetDefaultConstructor(Type Type)
Gets the default constructor of a type, if one exists.
Definition: Types.cs:1742
Class that can be used to schedule events in time. It uses a timer to execute tasks at the appointed ...
Definition: Scheduler.cs:14
bool Remove(DateTime When)
Removes an event scheduled for a given point in time.
Definition: Scheduler.cs:186
void Dispose()
IDisposable.Dispose
Definition: Scheduler.cs:34
DateTime Add(DateTime When, Action< object > Callback, object State)
Adds an event.
Definition: Scheduler.cs:54
Contains methods for simple hash calculations.
Definition: Hashes.cs:57
static byte[] ComputeSHA256Hash(byte[] Data)
Computes the SHA-256 hash of a block of binary data.
Definition: Hashes.cs:469
Interface for asynchronously disposable objects.
Interface for cluster commands.
Interface for cluster messages.
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
Definition: ISniffer.cs:10
Definition: ImplTypes.g.cs:58
delegate Task EventHandlerAsync(object Sender, EventArgs e)
Asynchronous version of EventArgs.
RemovedReason
Reason for removing the item.