用c#开发安卓程序 (xamarin.android)

作为c#程序员,没有精力和激情去学习java了,又遇到一些项目需要开发手机端,于是我们的networkcomms2.3.1网络通讯框架又要出场了,是的,这是一款来自英国的网络通讯框架,由c#语言编写,其在编写时根据不用的应用环境,写了不同的代码,支持安卓,平果,winphone等平台开发。

找了一个类大家看看他的书写方法 ,使用预编译语句,编写针对不同系统的代码

   1  public static class NetworkComms
   2     {
   3         /// <summary>
   4         /// Static constructor which sets comm default values
   5         /// </summary>
   6         static NetworkComms()
   7         {
   8             //Generally comms defaults are defined here
   9             NetworkIdentifier = ShortGuid.NewGuid();
  10             NetworkLoadUpdateWindowMS = 2000;
  11
  12             InterfaceLinkSpeed = 95000000;
  13
  14             DefaultListenPort = 10000;
  15             ListenOnAllAllowedInterfaces = true;
  16
  17             CheckSumMismatchSentPacketCacheMaxByteLimit = 75000;
  18             MinimumSentPacketCacheTimeMinutes = 1;
  19
  20             ConnectionEstablishTimeoutMS = 10000;
  21             PacketConfirmationTimeoutMS = 5000;
  22             ConnectionAliveTestTimeoutMS = 1000;
  23
  24 #if SILVERLIGHT || WINDOWS_PHONE
  25             CurrentRuntimeEnvironment = RuntimeEnvironment.WindowsPhone_Silverlight;
  26             SendBufferSizeBytes = ReceiveBufferSizeBytes = 8000;
  27 #elif iOS
  28             CurrentRuntimeEnvironment = RuntimeEnvironment.Xamarin_iOS;
  29             SendBufferSizeBytes = ReceiveBufferSizeBytes = 8000;
  30 #elif ANDROID
  31             CurrentRuntimeEnvironment = RuntimeEnvironment.Xamarin_Android;
  32             SendBufferSizeBytes = ReceiveBufferSizeBytes = 8000;
  33 #elif NET2
  34             if (Type.GetType("Mono.Runtime") != null)
  35             {
  36                 CurrentRuntimeEnvironment = RuntimeEnvironment.Mono_Net2;
  37                 //Mono send buffer smaller as different large object heap limit
  38                 SendBufferSizeBytes = ReceiveBufferSizeBytes = 8000;
  39             }
  40             else
  41             {
  42                 CurrentRuntimeEnvironment = RuntimeEnvironment.Native_Net2;
  43                 SendBufferSizeBytes = ReceiveBufferSizeBytes = 80000;
  44             }
  45 #elif NET35
  46             if (Type.GetType("Mono.Runtime") != null)
  47             {
  48                 CurrentRuntimeEnvironment = RuntimeEnvironment.Mono_Net35;
  49                 //Mono send buffer smaller as different large object heap limit
  50                 SendBufferSizeBytes = ReceiveBufferSizeBytes = 8000;
  51             }
  52             else
  53             {
  54                 CurrentRuntimeEnvironment = RuntimeEnvironment.Native_Net35;
  55                 SendBufferSizeBytes = ReceiveBufferSizeBytes = 80000;
  56             }
  57 #else
  58             if (Type.GetType("Mono.Runtime") != null)
  59             {
  60                 CurrentRuntimeEnvironment = RuntimeEnvironment.Mono_Net4;
  61                 //Mono send buffer smaller as different large object heap limit
  62                 SendBufferSizeBytes = ReceiveBufferSizeBytes = 8000;
  63             }
  64             else
  65             {
  66                 CurrentRuntimeEnvironment = RuntimeEnvironment.Native_Net4;
  67                 SendBufferSizeBytes = ReceiveBufferSizeBytes = 80000;
  68             }
  69 #endif
  70
  71             //We want to instantiate our own thread pool here
  72             CommsThreadPool = new CommsThreadPool(1, Environment.ProcessorCount*2, Environment.ProcessorCount * 20, new TimeSpan(0, 0, 10));
  73
  74             //Initialise the core extensions
  75             DPSManager.AddDataSerializer<ProtobufSerializer>();
  76
  77             DPSManager.AddDataSerializer<NullSerializer>();
  78             DPSManager.AddDataProcessor<SevenZipLZMACompressor.LZMACompressor>();
  79
  80 #if !FREETRIAL
  81             //Only the full version includes the encrypter
  82             DPSManager.AddDataProcessor<RijndaelPSKEncrypter>();
  83 #endif
  84
  85 #if !WINDOWS_PHONE
  86             DPSManager.AddDataSerializer<BinaryFormaterSerializer>();
  87 #endif
  88
  89             InternalFixedSendReceiveOptions = new SendReceiveOptions(DPSManager.GetDataSerializer<ProtobufSerializer>(),
  90                 new List<DataProcessor>(),
  91                 new Dictionary<string, string>());
  92
  93             DefaultSendReceiveOptions = new SendReceiveOptions(DPSManager.GetDataSerializer<ProtobufSerializer>(),
  94                 new List<DataProcessor>() { DPSManager.GetDataProcessor<SevenZipLZMACompressor.LZMACompressor>() },
  95                 new Dictionary<string, string>());
  96         }
  97
  98         #region Local Host Information
  99         /// <summary>
 100         /// Returns the current machine hostname
 101         /// </summary>
 102         public static string HostName
 103         {
 104             get
 105             {
 106 #if WINDOWS_PHONE
 107                 return Windows.Networking.Connectivity.NetworkInformation.GetInternetConnectionProfile().ToString();
 108 #else
 109                 return Dns.GetHostName();
 110 #endif
 111             }
 112         }
 113
 114         /// <summary>
 115         /// If set NetworkCommsDotNet will only operate on matching IP Addresses. Also see <see cref="AllowedAdaptorNames"/>.
 116         /// Correct format is string[] { "192.168", "213.111.10" }. If multiple prefixes are provided the earlier prefix, if found, takes priority.
 117         /// </summary>
 118         public static string[] AllowedIPPrefixes { get; set; }
 119
 120         /// <summary>
 121         ///  If set NetworkCommsDotNet will only operate on specified adaptors. Correct format is string[] { "eth0", "en0", "wlan0" }.
 122         /// </summary>
 123         public static string[] AllowedAdaptorNames { get; set; }
 124
 125         /// <summary>
 126         /// Returns all allowed local IP addresses.
 127         /// If <see cref="AllowedAdaptorNames"/> has been set only returns IP addresses corresponding with specified adaptors.
 128         /// If <see cref="AllowedIPPrefixes"/> has been set only returns matching addresses ordered in descending preference. i.e. Most preffered at [0].
 129         /// </summary>
 130         /// <returns></returns>
 131         public static List<IPAddress> AllAllowedIPs()
 132         {
 133
 134 #if WINDOWS_PHONE
 135             //On windows phone we simply ignore ip addresses from the autoassigned range as well as those without a valid prefix
 136             List<IPAddress> allowedIPs = new List<IPAddress>();
 137
 138             foreach (var hName in Windows.Networking.Connectivity.NetworkInformation.GetHostNames())
 139             {
 140                 if (!hName.DisplayName.StartsWith("169.254"))
 141                 {
 142                     if (AllowedIPPrefixes != null)
 143                     {
 144                         bool valid = false;
 145
 146                         for (int i = 0; i < AllowedIPPrefixes.Length; i++)
 147                             valid |= hName.DisplayName.StartsWith(AllowedIPPrefixes[i]);
 148
 149                         if(valid)
 150                             allowedIPs.Add(IPAddress.Parse(hName.DisplayName));
 151                     }
 152                     else
 153                         allowedIPs.Add(IPAddress.Parse(hName.DisplayName));
 154                 }
 155             }
 156
 157             return allowedIPs;
 158 #else
 159
 160             //We want to ignore IP‘s that have been autoassigned
 161             //169.254.0.0
 162             IPAddress autoAssignSubnetv4 = new IPAddress(new byte[] { 169, 254, 0, 0 });
 163             //255.255.0.0
 164             IPAddress autoAssignSubnetMaskv4 = new IPAddress(new byte[] { 255, 255, 0, 0 });
 165
 166             List<IPAddress> validIPAddresses = new List<IPAddress>();
 167             IPComparer comparer = new IPComparer();
 168
 169 #if ANDROID
 170
 171             var iFaces = Java.Net.NetworkInterface.NetworkInterfaces;
 172             while (iFaces.HasMoreElements)
 173             {
 174                 bool interfaceValid = false;
 175                 var iFace = iFaces.NextElement() as Java.Net.NetworkInterface;
 176                 var javaAddresses = iFace.InetAddresses;
 177
 178                 while (javaAddresses.HasMoreElements)
 179                 {
 180                     var javaAddress = javaAddresses.NextElement() as Java.Net.InetAddress;
 181                     IPAddress address = default(IPAddress);
 182                     if (IPAddress.TryParse(javaAddress.HostAddress, out address))
 183                     {
 184                         if (address.AddressFamily == AddressFamily.InterNetwork || address.AddressFamily == AddressFamily.InterNetworkV6)
 185                         {
 186                             if (AllowedAdaptorNames != null)
 187                             {
 188                                 foreach (var id in AllowedAdaptorNames)
 189                                     if (id == iFace.Name)
 190                                     {
 191                                         interfaceValid = true;
 192                                         break;
 193                                     }
 194                             }
 195                             else
 196                                 interfaceValid = true;
 197
 198                             if (interfaceValid)
 199                                 break;
 200                         }
 201                     }
 202                 }
 203
 204                 if (!interfaceValid)
 205                     continue;
 206
 207                 javaAddresses = iFace.InetAddresses;
 208
 209                 while (javaAddresses.HasMoreElements)
 210                 {
 211                     var javaAddress = javaAddresses.NextElement() as Java.Net.InetAddress;
 212                     IPAddress address = default(IPAddress);
 213
 214                     if (IPAddress.TryParse(javaAddress.HostAddress, out address))
 215                     {
 216                         if (address.AddressFamily == AddressFamily.InterNetwork || address.AddressFamily == AddressFamily.InterNetworkV6)
 217                         {
 218                             if (!IsAddressInSubnet(address, autoAssignSubnetv4, autoAssignSubnetMaskv4))
 219                             {
 220                                 bool allowed = false;
 221
 222                                 if (AllowedAdaptorNames != null)
 223                                 {
 224                                     foreach (var id in AllowedAdaptorNames)
 225                                     {
 226                                         if (id == iFace.Name)
 227                                         {
 228                                             allowed = true;
 229                                             break;
 230                                         }
 231                                     }
 232                                 }
 233                                 else
 234                                     allowed = true;
 235
 236                                 if (!allowed)
 237                                     continue;
 238
 239                                 allowed = false;
 240
 241                                 if (AllowedIPPrefixes != null)
 242                                 {
 243                                     foreach (var ip in AllowedIPPrefixes)
 244                                     {
 245                                         if (comparer.Equals(address.ToString(), ip))
 246                                         {
 247                                             allowed = true;
 248                                             break;
 249                                         }
 250                                     }
 251                                 }
 252                                 else
 253                                     allowed = true;
 254
 255                                 if (!allowed)
 256                                     continue;
 257
 258                                 if (address != IPAddress.None)
 259                                     validIPAddresses.Add(address);
 260                             }
 261                         }
 262                     }
 263                 }
 264             }
 265
 266 #else
 267
 268
 269             foreach (var iFace in NetworkInterface.GetAllNetworkInterfaces())
 270             {
 271                 bool interfaceValid = false;
 272                 var unicastAddresses = iFace.GetIPProperties().UnicastAddresses;
 273
 274                 foreach (var address in unicastAddresses)
 275                 {
 276                     if (address.Address.AddressFamily == AddressFamily.InterNetwork || address.Address.AddressFamily == AddressFamily.InterNetworkV6)
 277                     {
 278                         if (AllowedAdaptorNames != null)
 279                         {
 280                             foreach (var id in AllowedAdaptorNames)
 281                                 if (iFace.Id == id)
 282                                 {
 283                                     interfaceValid = true;
 284                                     break;
 285                                 }
 286                         }
 287                         else
 288                             interfaceValid = true;
 289
 290                         if (interfaceValid)
 291                             break;
 292                     }
 293                 }
 294
 295                 if (!interfaceValid)
 296                     continue;
 297
 298                 foreach (var address in unicastAddresses)
 299                 {
 300                     var addressInformation = address.Address;
 301                     if (addressInformation.AddressFamily == AddressFamily.InterNetwork || addressInformation.AddressFamily == AddressFamily.InterNetworkV6)
 302                     {
 303                         if (!IsAddressInSubnet(addressInformation, autoAssignSubnetv4, autoAssignSubnetMaskv4))
 304                         {
 305                             bool allowed = false;
 306
 307                             if (AllowedAdaptorNames != null)
 308                             {
 309                                 foreach (var id in AllowedAdaptorNames)
 310                                 {
 311                                     if(id == iFace.Id)
 312                                     {
 313                                         allowed = true;
 314                                         break;
 315                                     }
 316                                 }
 317                             }
 318                             else
 319                                 allowed = true;
 320
 321                             if (!allowed)
 322                                 continue;
 323
 324                             allowed = false;
 325
 326                             if (AllowedIPPrefixes != null)
 327                             {
 328                                 foreach (var ip in AllowedIPPrefixes)
 329                                 {
 330                                     if (comparer.Equals(addressInformation.ToString(), ip))
 331                                     {
 332                                         allowed = true;
 333                                         break;
 334                                     }
 335                                 }
 336                             }
 337                             else
 338                                 allowed = true;
 339
 340                             if (!allowed)
 341                                 continue;
 342
 343                             if (addressInformation != IPAddress.None)
 344                                 validIPAddresses.Add(addressInformation);
 345                         }
 346                     }
 347                 }
 348             }
 349 #endif
 350
 351             if (AllowedIPPrefixes != null)
 352             {
 353                 validIPAddresses.Sort((a, b) =>
 354                 {
 355                     for (int i = 0; i < AllowedIPPrefixes.Length; i++)
 356                     {
 357                         if (a.ToString().StartsWith(AllowedIPPrefixes[i]))
 358                         {
 359                             if (b.ToString().StartsWith(AllowedIPPrefixes[i]))
 360                                 return 0;
 361                             else
 362                                 return -1;
 363                         }
 364                         else if (b.ToString().StartsWith(AllowedIPPrefixes[i]))
 365                             return 1;
 366                     }
 367
 368                     return 0;
 369                 });
 370             }
 371
 372             return validIPAddresses;
 373 #endif
 374         }
 375
 376         /// <summary>
 377         /// Custom comparer for IP addresses. Used by <see cref="AllAllowedIPs"/>
 378         /// </summary>
 379         class IPComparer : IEqualityComparer<string>
 380         {
 381             // Products are equal if their names and product numbers are equal.
 382             public bool Equals(string x, string y)
 383             {
 384                 //Check whether the compared objects reference the same data.
 385                 if (Object.ReferenceEquals(x, y)) return true;
 386
 387                 //Check whether any of the compared objects is null.
 388                 if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
 389                     return false;
 390
 391                 return (y.StartsWith(x) || x.StartsWith(y));
 392             }
 393
 394             // If Equals() returns true for a pair of objects
 395             // then GetHashCode() must return the same value for these objects.
 396             public int GetHashCode(string ipAddress)
 397             {
 398                 return ipAddress.GetHashCode();
 399             }
 400         }
 401
 402         /// <summary>
 403         /// Returns true if the provided address exists within the provided subnet.
 404         /// </summary>
 405         /// <param name="address">The address to check, i.e. 192.168.0.10</param>
 406         /// <param name="subnet">The subnet, i.e. 192.168.0.0</param>
 407         /// <param name="mask">The subnet mask, i.e. 255.255.255.0</param>
 408         /// <returns>True if address is in the provided subnet</returns>
 409         public static bool IsAddressInSubnet(IPAddress address, IPAddress subnet, IPAddress mask)
 410         {
 411             if (address == null) throw new ArgumentNullException("address", "Provided IPAddress cannot be null.");
 412             if (subnet == null) throw new ArgumentNullException("subnet", "Provided IPAddress cannot be null.");
 413             if (mask == null) throw new ArgumentNullException("mask", "Provided IPAddress cannot be null.");
 414
 415             //Catch for IPv6
 416             if (subnet.AddressFamily == AddressFamily.InterNetworkV6 ||
 417                 mask.AddressFamily == AddressFamily.InterNetworkV6)
 418                 throw new NotImplementedException("This method does not yet support IPv6. Please contact NetworkComms.Net support if you would like this functionality.");
 419             //If we have provided IPV4 subnets and masks and we have an ipv6 address then return false
 420             else if (address.AddressFamily == AddressFamily.InterNetworkV6)
 421                 return false;
 422
 423             byte[] addrBytes = address.GetAddressBytes();
 424             byte[] maskBytes = mask.GetAddressBytes();
 425             byte[] maskedAddressBytes = new byte[addrBytes.Length];
 426
 427             //Catch for IPv6
 428             if (maskBytes.Length < maskedAddressBytes.Length)
 429                 return false;
 430
 431             for (int i = 0; i < maskedAddressBytes.Length; ++i)
 432                 maskedAddressBytes[i] = (byte)(addrBytes[i] & maskBytes[i]);
 433
 434             IPAddress maskedAddress = new IPAddress(maskedAddressBytes);
 435             bool equal = subnet.Equals(maskedAddress);
 436
 437             return equal;
 438         }
 439
 440         /// <summary>
 441         /// The default port NetworkCommsDotNet will operate on
 442         /// </summary>
 443         public static int DefaultListenPort { get; set; }
 444
 445         /// <summary>
 446         /// The local identifier for this instance of NetworkCommsDotNet. This is an application unique identifier.
 447         /// </summary>
 448         public static ShortGuid NetworkIdentifier { get; private set; }
 449
 450         /// <summary>
 451         /// The current runtime environment. Detected automatically on startup. Performance may be adversly affected if this is changed.
 452         /// </summary>
 453         public static RuntimeEnvironment CurrentRuntimeEnvironment { get; set; }
 454
 455         /// <summary>
 456         /// An internal random object
 457         /// </summary>
 458         internal static Random randomGen = new Random();
 459
 460         /// <summary>
 461         /// A single boolean used to control a NetworkCommsDotNet shutdown
 462         /// </summary>
 463         internal static volatile bool commsShutdown;
 464
 465         /// <summary>
 466         /// A running total of the number of packets sent on all connections. Used to initialise packet sequence counters to ensure duplicates can not occur.
 467         /// </summary>
 468         internal static long totalPacketSendCount;
 469
 470         /// <summary>
 471         /// The number of millisconds over which to take an instance load (CurrentNetworkLoad) to be used in averaged values (AverageNetworkLoad).
 472         /// Default is 2000ms. Shorter values can be used but less than 200ms may cause significant errors in the value of returned value, especially in mono environments.
 473         /// </summary>
 474         public static int NetworkLoadUpdateWindowMS { get; set; }
 475
 476         private static double currentNetworkLoadIncoming;
 477         private static double currentNetworkLoadOutgoing;
 478 #if !WINDOWS_PHONE && !ANDROID
 479         private static Thread NetworkLoadThread = null;
 480         private static CommsMath currentNetworkLoadValuesIncoming;
 481         private static CommsMath currentNetworkLoadValuesOutgoing;
 482         private static ManualResetEvent NetworkLoadThreadWait;
 483 #endif
 484
 485         /// <summary>
 486         /// The interface link speed in bits/sec used for network load calculations. Default is 100Mb/sec
 487         /// </summary>
 488         public static long InterfaceLinkSpeed { get; set; }
 489
 490         /// <summary>
 491         /// Returns the current instance network usage, as a value between 0 and 1. Returns the largest value for any available network adaptor. Triggers load analysis upon first call.
 492         /// </summary>
 493         public static double CurrentNetworkLoadIncoming
 494         {
 495             get
 496             {
 497 #if !WINDOWS_PHONE && !ANDROID
 498                 //We start the load thread when we first access the network load
 499                 //this helps cut down on uncessary threads if unrequired
 500                 if (!commsShutdown && NetworkLoadThread == null)
 501                 {
 502                     lock (globalDictAndDelegateLocker)
 503                     {
 504                         if (!commsShutdown && NetworkLoadThread == null)
 505                         {
 506                             currentNetworkLoadValuesIncoming = new CommsMath();
 507                             currentNetworkLoadValuesOutgoing = new CommsMath();
 508
 509                             NetworkLoadThread = new Thread(NetworkLoadWorker);
 510                             NetworkLoadThread.Name = "NetworkLoadThread";
 511                             NetworkLoadThread.Start();
 512                         }
 513                     }
 514                 }
 515 #endif
 516                 return currentNetworkLoadIncoming;
 517             }
 518             private set { currentNetworkLoadIncoming = value; }
 519         }
 520
 521         /// <summary>
 522         /// Returns the current instance network usage, as a value between 0 and 1. Returns the largest value for any available network adaptor. Triggers load analysis upon first call.
 523         /// </summary>
 524         public static double CurrentNetworkLoadOutgoing
 525         {
 526             get
 527             {
 528 #if !WINDOWS_PHONE && !ANDROID
 529                 //We start the load thread when we first access the network load
 530                 //this helps cut down on uncessary threads if unrequired
 531                 if (!commsShutdown && NetworkLoadThread == null)
 532                 {
 533                     lock (globalDictAndDelegateLocker)
 534                     {
 535                         if (!commsShutdown && NetworkLoadThread == null)
 536                         {
 537                             currentNetworkLoadValuesIncoming = new CommsMath();
 538                             currentNetworkLoadValuesOutgoing = new CommsMath();
 539
 540                             NetworkLoadThread = new Thread(NetworkLoadWorker);
 541                             NetworkLoadThread.Name = "NetworkLoadThread";
 542                             NetworkLoadThread.Start();
 543                         }
 544                     }
 545                 }
 546 #endif
 547                 return currentNetworkLoadOutgoing;
 548             }
 549             private set { currentNetworkLoadOutgoing = value; }
 550         }
 551
 552         /// <summary>
 553         /// Returns the averaged value of CurrentNetworkLoadIncoming, as a value between 0 and 1, for a time window of upto 254 seconds. Triggers load analysis upon first call.
 554         /// </summary>
 555         /// <param name="secondsToAverage">Number of seconds over which historial data should be used to arrive at an average</param>
 556         /// <returns>Average network load as a double between 0 and 1</returns>
 557         public static double AverageNetworkLoadIncoming(byte secondsToAverage)
 558         {
 559 #if !WINDOWS_PHONE && !ANDROID
 560
 561             if (!commsShutdown && NetworkLoadThread == null)
 562             {
 563                 lock (globalDictAndDelegateLocker)
 564                 {
 565                     if (!commsShutdown && NetworkLoadThread == null)
 566                     {
 567                         currentNetworkLoadValuesIncoming = new CommsMath();
 568                         currentNetworkLoadValuesOutgoing = new CommsMath();
 569
 570                         NetworkLoadThread = new Thread(NetworkLoadWorker);
 571                         NetworkLoadThread.Name = "NetworkLoadThread";
 572                         NetworkLoadThread.Start();
 573                     }
 574                 }
 575             }
 576
 577             return currentNetworkLoadValuesIncoming.CalculateMean((int)((secondsToAverage * 1000.0) / NetworkLoadUpdateWindowMS));
 578 #else
 579             return 0;
 580 #endif
 581         }
 582
 583         /// <summary>
 584         /// Returns the averaged value of CurrentNetworkLoadIncoming, as a value between 0 and 1, for a time window of upto 254 seconds. Triggers load analysis upon first call.
 585         /// </summary>
 586         /// <param name="secondsToAverage">Number of seconds over which historial data should be used to arrive at an average</param>
 587         /// <returns>Average network load as a double between 0 and 1</returns>
 588         public static double AverageNetworkLoadOutgoing(byte secondsToAverage)
 589         {
 590 #if !WINDOWS_PHONE && !ANDROID
 591             if (!commsShutdown && NetworkLoadThread == null)
 592             {
 593                 lock (globalDictAndDelegateLocker)
 594                 {
 595                     if (!commsShutdown && NetworkLoadThread == null)
 596                     {
 597                         currentNetworkLoadValuesIncoming = new CommsMath();
 598                         currentNetworkLoadValuesOutgoing = new CommsMath();
 599
 600                         NetworkLoadThread = new Thread(NetworkLoadWorker);
 601                         NetworkLoadThread.Name = "NetworkLoadThread";
 602                         NetworkLoadThread.Start();
 603                     }
 604                 }
 605             }
 606
 607             return currentNetworkLoadValuesOutgoing.CalculateMean((int)((secondsToAverage * 1000.0) / NetworkLoadUpdateWindowMS));
 608 #else
 609             return 0;
 610 #endif
 611         }
 612
 613         /// <summary>
 614         /// Determines the most appropriate local end point to contact the provided remote end point.
 615         /// Testing shows this method takes on average 1.6ms to return.
 616         /// </summary>
 617         /// <param name="remoteIPEndPoint">The remote end point</param>
 618         /// <returns>The selected local end point</returns>
 619         public static IPEndPoint BestLocalEndPoint(IPEndPoint remoteIPEndPoint)
 620         {
 621             if (remoteIPEndPoint == null) throw new ArgumentNullException("remoteIPEndPoint", "Provided IPEndPoint cannot be null.");
 622
 623 #if WINDOWS_PHONE
 624             var t = Windows.Networking.Sockets.DatagramSocket.GetEndpointPairsAsync(new Windows.Networking.HostName(remoteIPEndPoint.Address.ToString()), remoteIPEndPoint.Port.ToString()).AsTask();
 625             if (t.Wait(20) && t.Result.Count > 0)
 626             {
 627                 var enumerator = t.Result.GetEnumerator();
 628                 enumerator.MoveNext();
 629
 630                 var endpointPair = enumerator.Current;
 631                 return new IPEndPoint(IPAddress.Parse(endpointPair.LocalHostName.DisplayName.ToString()), int.Parse(endpointPair.LocalServiceName));
 632             }
 633             else
 634                 throw new ConnectionSetupException("Unable to determine correct local end point.");
 635 #else
 636             //We use UDP as its connectionless hence faster
 637             IPEndPoint result;
 638             using (Socket testSocket = new Socket(remoteIPEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp))
 639             {
 640                 testSocket.Connect(remoteIPEndPoint);
 641                 result = (IPEndPoint)testSocket.LocalEndPoint;
 642             }
 643
 644             return result;
 645 #endif
 646         }
 647
 648 #if !WINDOWS_PHONE && !ANDROID
 649         /// <summary>
 650         /// Takes a network load snapshot (CurrentNetworkLoad) every NetworkLoadUpdateWindowMS
 651         /// </summary>
 652         private static void NetworkLoadWorker()
 653         {
 654             NetworkLoadThreadWait = new ManualResetEvent(false);
 655
 656             //Get all interfaces
 657             NetworkInterface[] interfacesToUse = NetworkInterface.GetAllNetworkInterfaces();
 658
 659             long[] startSent, startReceived, endSent, endReceived;
 660
 661             while (!commsShutdown)
 662             {
 663                 try
 664                 {
 665                     //we need to look at the load across all adaptors, by default we will probably choose the adaptor with the highest usage
 666                     DateTime startTime = DateTime.Now;
 667
 668                     IPv4InterfaceStatistics[] stats = new IPv4InterfaceStatistics[interfacesToUse.Length];
 669                     startSent = new long[interfacesToUse.Length];
 670                     startReceived = new long[interfacesToUse.Length];
 671
 672                     for (int i = 0; i < interfacesToUse.Length; ++i)
 673                     {
 674                         stats[i] = interfacesToUse[i].GetIPv4Statistics();
 675                         startSent[i] = stats[i].BytesSent;
 676                         startReceived[i] = stats[i].BytesReceived;
 677                     }
 678
 679                     if (commsShutdown) return;
 680
 681                     //Thread.Sleep(NetworkLoadUpdateWindowMS);
 682                     NetworkLoadThreadWait.WaitOne(NetworkLoadUpdateWindowMS);
 683
 684                     if (commsShutdown) return;
 685
 686                     stats = new IPv4InterfaceStatistics[interfacesToUse.Length];
 687                     endSent = new long[interfacesToUse.Length];
 688                     endReceived = new long[interfacesToUse.Length];
 689
 690                     for (int i = 0; i < interfacesToUse.Length; ++i)
 691                     {
 692                         stats[i] = interfacesToUse[i].GetIPv4Statistics();
 693                         endSent[i] = stats[i].BytesSent;
 694                         endReceived[i] = stats[i].BytesReceived;
 695                     }
 696
 697                     DateTime endTime = DateTime.Now;
 698
 699                     List<double> outUsage = new List<double>();
 700                     List<double> inUsage = new List<double>();
 701                     for(int i=0; i<startSent.Length; i++)
 702                     {
 703                         outUsage.Add((double)(endSent[i] - startSent[i]) / ((double)(InterfaceLinkSpeed * (endTime - startTime).TotalMilliseconds) / 8000));
 704                         inUsage.Add((double)(endReceived[i] - startReceived[i]) / ((double)(InterfaceLinkSpeed * (endTime - startTime).TotalMilliseconds) / 8000));
 705                     }
 706
 707                     //double loadValue = Math.Max(outUsage.Max(), inUsage.Max());
 708                     double inMax = double.MinValue, outMax = double.MinValue;
 709                     for (int i = 0; i < startSent.Length; ++i)
 710                     {
 711                         if (inUsage[i] > inMax) inMax = inUsage[i];
 712                         if (outUsage[i] > outMax) outMax = outUsage[i];
 713                     }
 714
 715                     //If either of the usage levels have gone above 2 it suggests we are most likely on a faster connection that we think
 716                     //As such we will bump the interfacelinkspeed upto 1Gbps so that future load calcualtions more acurately reflect the
 717                     //actual load.
 718                     if (inMax > 2 || outMax > 2) InterfaceLinkSpeed = 950000000;
 719
 720                     //Limit to one
 721                     CurrentNetworkLoadIncoming = (inMax > 1 ? 1 : inMax);
 722                     CurrentNetworkLoadOutgoing = (outMax > 1 ? 1 : outMax);
 723
 724                     currentNetworkLoadValuesIncoming.AddValue(CurrentNetworkLoadIncoming);
 725                     currentNetworkLoadValuesOutgoing.AddValue(CurrentNetworkLoadOutgoing);
 726
 727                     //We can only have upto 255 seconds worth of data in the average list
 728                     int maxListSize = (int)(255000.0 / NetworkLoadUpdateWindowMS);
 729                     currentNetworkLoadValuesIncoming.TrimList(maxListSize);
 730                     currentNetworkLoadValuesOutgoing.TrimList(maxListSize);
 731                 }
 732                 catch (Exception ex)
 733                 {
 734                     LogError(ex, "NetworkLoadWorker");
 735
 736                     //It may be the interfaces available to the OS have changed so we will reset them here
 737                     interfacesToUse = NetworkInterface.GetAllNetworkInterfaces();
 738                     //If an error has happened we dont want to thrash the problem, we wait for 5 seconds and hope whatever was wrong goes away
 739                     Thread.Sleep(5000);
 740                 }
 741             }
 742         }
 743 #endif
 744         #endregion
 745
 746         #region Established Connections
 747         /// <summary>
 748         /// Locker for connection dictionaries
 749         /// </summary>
 750         internal static object globalDictAndDelegateLocker = new object();
 751
 752         /// <summary>
 753         /// Primary connection dictionary stored by network indentifier
 754         /// </summary>
 755         internal static Dictionary<ShortGuid, Dictionary<ConnectionType, List<Connection>>> allConnectionsById = new Dictionary<ShortGuid, Dictionary<ConnectionType, List<Connection>>>();
 756
 757         /// <summary>
 758         /// Secondary connection dictionary stored by ip end point. Allows for quick cross referencing.
 759         /// </summary>
 760         internal static Dictionary<IPEndPoint, Dictionary<ConnectionType, Connection>> allConnectionsByEndPoint = new Dictionary<IPEndPoint, Dictionary<ConnectionType, Connection>>();
 761
 762         /// <summary>
 763         /// Old connection cache so that requests for connectionInfo can be returned even after a connection has been closed.
 764         /// </summary>
 765         internal static Dictionary<ShortGuid, Dictionary<ConnectionType, List<ConnectionInfo>>> oldNetworkIdentifierToConnectionInfo = new Dictionary<ShortGuid, Dictionary<ConnectionType, List<ConnectionInfo>>>();
 766         #endregion
 767
 768         #region Incoming Data and Connection Config
 769         /// <summary>
 770         /// Used for switching between async and sync connectionListen modes. Default is false. No noticable performance difference between the two modes.
 771         /// </summary>
 772         public static bool ConnectionListenModeUseSync { get; set; }
 773
 774         /// <summary>
 775         /// Used for switching between listening on a single interface or multiple interfaces. Default is true. See <see cref="AllowedIPPrefixes"/> and <see cref="AllowedAdaptorNames"/>
 776         /// </summary>
 777         public static bool ListenOnAllAllowedInterfaces { get; set; }
 778
 779         /// <summary>
 780         /// Receive data buffer size. Default is 80KB. CAUTION: Changing the default value can lead to performance degredation.
 781         /// </summary>
 782         public static int ReceiveBufferSizeBytes { get; set; }
 783
 784         /// <summary>
 785         /// Send data buffer size. Default is 80KB. CAUTION: Changing the default value can lead to performance degredation.
 786         /// </summary>
 787         public static int SendBufferSizeBytes { get; set; }
 788
 789         /// <summary>
 790         /// The threadpool used by networkComms.Net to execute incoming packet handlers.
 791         /// </summary>
 792         public static CommsThreadPool CommsThreadPool { get; set; }
 793
 794         /// <summary>
 795         /// Once we have received all incoming data we handle it further. This is performed at the global level to help support different priorities.
 796         /// </summary>
 797         /// <param name="itemAsObj">Possible PriorityQueueItem. If null is provided an item will be removed from the global item queue</param>
 798         internal static void CompleteIncomingItemTask(object itemAsObj)
 799         {
 800             if (itemAsObj == null)
 801                 throw new ArgumentNullException("itemAsObj", "Provided parameter itemAsObj cannot be null.");
 802
 803             PriorityQueueItem item = null;
 804             try
 805             {
 806                 //If the packetBytes are null we need to ask the incoming packet queue for what we should be running
 807                 item = itemAsObj as PriorityQueueItem;
 808
 809                 if (item == null)
 810                     throw new InvalidCastException("Cast from object to PriorityQueueItem resulted in null reference, unable to continue.");
 811
 812                 if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Handling a " + item.PacketHeader.PacketType + " packet from " + item.Connection.ConnectionInfo + " with a priority of " + item.Priority.ToString() + ".");
 813
 814 #if !WINDOWS_PHONE
 815                 if (Thread.CurrentThread.Priority != (ThreadPriority)item.Priority) Thread.CurrentThread.Priority = (ThreadPriority)item.Priority;
 816 #endif
 817
 818                 //Check for a shutdown connection
 819                 if (item.Connection.ConnectionInfo.ConnectionState == ConnectionState.Shutdown) return;
 820
 821                 //We only look at the check sum if we want to and if it has been set by the remote end
 822                 if (NetworkComms.EnablePacketCheckSumValidation && item.PacketHeader.ContainsOption(PacketHeaderStringItems.CheckSumHash))
 823                 {
 824                     var packetHeaderHash = item.PacketHeader.GetOption(PacketHeaderStringItems.CheckSumHash);
 825
 826                     //Validate the checkSumhash of the data
 827                     string packetDataSectionMD5 = NetworkComms.MD5Bytes(item.DataStream);
 828                     if (packetHeaderHash != packetDataSectionMD5)
 829                     {
 830                         if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Warn(" ... corrupted packet detected, expected " + packetHeaderHash + " but received " + packetDataSectionMD5 + ".");
 831
 832                         //We have corruption on a resend request, something is very wrong so we throw an exception.
 833                         if (item.PacketHeader.PacketType == Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.CheckSumFailResend)) throw new CheckSumException("Corrupted md5CheckFailResend packet received.");
 834
 835                         if (item.PacketHeader.PayloadPacketSize < NetworkComms.CheckSumMismatchSentPacketCacheMaxByteLimit)
 836                         {
 837                             //Instead of throwing an exception we can request the packet to be resent
 838                             Packet returnPacket = new Packet(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.CheckSumFailResend), packetHeaderHash, NetworkComms.InternalFixedSendReceiveOptions);
 839                             item.Connection.SendPacket(returnPacket);
 840                             //We need to wait for the packet to be resent before going further
 841                             return;
 842                         }
 843                         else
 844                             throw new CheckSumException("Corrupted packet detected from " + item.Connection.ConnectionInfo + ", expected " + packetHeaderHash + " but received " + packetDataSectionMD5 + ".");
 845                     }
 846                 }
 847
 848                 //Remote end may have requested packet receive confirmation so we send that now
 849                 if (item.PacketHeader.ContainsOption(PacketHeaderStringItems.ReceiveConfirmationRequired))
 850                 {
 851                     if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... sending requested receive confirmation packet.");
 852
 853                     var hash = item.PacketHeader.ContainsOption(PacketHeaderStringItems.CheckSumHash) ? item.PacketHeader.GetOption(PacketHeaderStringItems.CheckSumHash) : "";
 854
 855                     Packet returnPacket = new Packet(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.Confirmation), hash, NetworkComms.InternalFixedSendReceiveOptions);
 856                     item.Connection.SendPacket(returnPacket);
 857                 }
 858
 859                 //We can now pass the data onto the correct delegate
 860                 //First we have to check for our reserved packet types
 861                 //The following large sections have been factored out to make reading and debugging a little easier
 862                 if (item.PacketHeader.PacketType == Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.CheckSumFailResend))
 863                     item.Connection.CheckSumFailResendHandler(item.DataStream);
 864                 else if (item.PacketHeader.PacketType == Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.ConnectionSetup))
 865                     item.Connection.ConnectionSetupHandler(item.DataStream);
 866                 else if (item.PacketHeader.PacketType == Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.AliveTestPacket) &&
 867                     (NetworkComms.InternalFixedSendReceiveOptions.DataSerializer.DeserialiseDataObject<byte[]>(item.DataStream,
 868                         NetworkComms.InternalFixedSendReceiveOptions.DataProcessors,
 869                         NetworkComms.InternalFixedSendReceiveOptions.Options))[0] == 0)
 870                 {
 871                     //If we have received a ping packet from the originating source we reply with true
 872                     Packet returnPacket = new Packet(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.AliveTestPacket), new byte[1] { 1 }, NetworkComms.InternalFixedSendReceiveOptions);
 873                     item.Connection.SendPacket(returnPacket);
 874                 }
 875
 876                 //We allow users to add their own custom handlers for reserved packet types here
 877                 //else
 878                 if (true)
 879                 {
 880                     if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Triggering handlers for packet of type ‘" + item.PacketHeader.PacketType + "‘ from " + item.Connection.ConnectionInfo);
 881
 882                     //We trigger connection specific handlers first
 883                     bool connectionSpecificHandlersTriggered = item.Connection.TriggerSpecificPacketHandlers(item.PacketHeader, item.DataStream, item.SendReceiveOptions);
 884
 885                     //We trigger global handlers second
 886                     NetworkComms.TriggerGlobalPacketHandlers(item.PacketHeader, item.Connection, item.DataStream, item.SendReceiveOptions, connectionSpecificHandlersTriggered);
 887
 888                     //This is a really bad place to put a garbage collection, comment left in so that it doesn‘t get added again at some later date
 889                     //We don‘t want the CPU to JUST be trying to garbage collect the WHOLE TIME
 890                     //GC.Collect();
 891                 }
 892             }
 893             catch (CommunicationException)
 894             {
 895                 if (item != null)
 896                 {
 897                     if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Fatal("A communcation exception occured in CompleteIncomingPacketWorker(), connection with " + item.Connection.ConnectionInfo + " be closed.");
 898                     item.Connection.CloseConnection(true, 2);
 899                 }
 900             }
 901             catch (DuplicateConnectionException ex)
 902             {
 903                 if (item != null)
 904                 {
 905                     if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Warn(ex.Message != null ? ex.Message : "A possible duplicate connection was detected with " + item.Connection + ". Closing connection.");
 906                     item.Connection.CloseConnection(true, 42);
 907                 }
 908             }
 909             catch (Exception ex)
 910             {
 911                 NetworkComms.LogError(ex, "CompleteIncomingItemTaskError");
 912
 913                 if (item != null)
 914                 {
 915                     //If anything goes wrong here all we can really do is log the exception
 916                     if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Fatal("An unhandled exception occured in CompleteIncomingPacketWorker(), connection with " + item.Connection.ConnectionInfo + " be closed. See log file for more information.");
 917                     item.Connection.CloseConnection(true, 3);
 918                 }
 919             }
 920             finally
 921             {
 922                 //We need to dispose the data stream correctly
 923                 if (item!=null) item.DataStream.Close();
 924
 925 #if !WINDOWS_PHONE
 926                 //Ensure the thread returns to the pool with a normal priority
 927                 if (Thread.CurrentThread.Priority != ThreadPriority.Normal) Thread.CurrentThread.Priority = ThreadPriority.Normal;
 928 #endif
 929             }
 930         }
 931         #endregion
 932
 933 #if !WINDOWS_PHONE
 934         #region High CPU Usage Tuning
 935         /// <summary>
 936         /// In times of high CPU usage we need to ensure that certain time critical functions, like connection handshaking do not timeout.
 937         /// This sets the thread priority for those processes.
 938         /// </summary>
 939         internal static ThreadPriority timeCriticalThreadPriority = ThreadPriority.AboveNormal;
 940         #endregion
 941 #endif
 942
 943         #region Checksum Config
 944         /// <summary>
 945         /// When enabled uses an MD5 checksum to validate all received packets. Default is false, relying on any possible connection checksum alone.
 946         /// Also when enabled any packets sent less than CheckSumMismatchSentPacketCacheMaxByteLimit will be cached for a duration to ensure successful delivery.
 947         /// Default false.
 948         /// </summary>
 949         public static bool EnablePacketCheckSumValidation { get; set; }
 950
 951         /// <summary>
 952         /// When checksum validation is enabled sets the limit below which sent packets are cached to ensure successful delivery. Default 75KB.
 953         /// </summary>
 954         public static int CheckSumMismatchSentPacketCacheMaxByteLimit { get; set; }
 955
 956         /// <summary>
 957         /// When a sent packet has been cached for a possible resend this is the minimum length of time it will be retained. Default is 1.0 minutes.
 958         /// </summary>
 959         public static double MinimumSentPacketCacheTimeMinutes { get; set; }
 960
 961         /// <summary>
 962         /// Records the last sent packet cache cleanup time. Prevents the sent packet cache from being checked too frequently.
 963         /// </summary>
 964         internal static DateTime LastSentPacketCacheCleanup { get; set; }
 965         #endregion
 966
 967         #region PacketType Config and Global Handlers
 968         /// <summary>
 969         /// An internal reference copy of all reservedPacketTypeNames.
 970         /// </summary>
 971         internal static string[] reservedPacketTypeNames = Enum.GetNames(typeof(ReservedPacketType));
 972
 973         /// <summary>
 974         /// Dictionary of all custom packetHandlers. Key is packetType.
 975         /// </summary>
 976         static Dictionary<string, List<IPacketTypeHandlerDelegateWrapper>> globalIncomingPacketHandlers = new Dictionary<string, List<IPacketTypeHandlerDelegateWrapper>>();
 977
 978         /// <summary>
 979         /// Dictionary of any non default custom packet unwrappers. Key is packetType.
 980         /// </summary>
 981         static Dictionary<string, PacketTypeUnwrapper> globalIncomingPacketUnwrappers = new Dictionary<string, PacketTypeUnwrapper>();
 982
 983         /// <summary>
 984         /// Delegate for handling incoming packets. See AppendGlobalIncomingPacketHandler members.
 985         /// </summary>
 986         /// <typeparam name="T">The type of object which is expected for this handler</typeparam>
 987         /// <param name="packetHeader">The <see cref="PacketHeader"/> of the incoming packet</param>
 988         /// <param name="connection">The connection with which this packet was received</param>
 989         /// <param name="incomingObject">The incoming object of specified type T</param>
 990         public delegate void PacketHandlerCallBackDelegate<T>(PacketHeader packetHeader, Connection connection, T incomingObject);
 991
 992         /// <summary>
 993         /// If true any unknown incoming packet types are ignored. Default is false and will result in an error file being created if an unknown packet type is received.
 994         /// </summary>
 995         public static bool IgnoreUnknownPacketTypes { get; set; }
 996
 997         /// <summary>
 998         /// Add an incoming packet handler using default SendReceiveOptions. Multiple handlers for the same packet type will be executed in the order they are added.
 999         /// </summary>
1000         /// <typeparam name="T">The type of incoming object</typeparam>
1001         /// <param name="packetTypeStr">The packet type for which this handler will be executed</param>
1002         /// <param name="packetHandlerDelgatePointer">The delegate to be executed when a packet of packetTypeStr is received</param>
1003         public static void AppendGlobalIncomingPacketHandler<T>(string packetTypeStr, PacketHandlerCallBackDelegate<T> packetHandlerDelgatePointer)
1004         {
1005             AppendGlobalIncomingPacketHandler<T>(packetTypeStr, packetHandlerDelgatePointer, DefaultSendReceiveOptions);
1006         }
1007
1008         /// <summary>
1009         /// Add an incoming packet handler using the provided SendReceiveOptions. Multiple handlers for the same packet type will be executed in the order they are added.
1010         /// </summary>
1011         /// <typeparam name="T">The type of incoming object</typeparam>
1012         /// <param name="packetTypeStr">The packet type for which this handler will be executed</param>
1013         /// <param name="packetHandlerDelgatePointer">The delegate to be executed when a packet of packetTypeStr is received</param>
1014         /// <param name="sendReceiveOptions">The SendReceiveOptions to be used for the provided packet type</param>
1015         public static void AppendGlobalIncomingPacketHandler<T>(string packetTypeStr, PacketHandlerCallBackDelegate<T> packetHandlerDelgatePointer, SendReceiveOptions sendReceiveOptions)
1016         {
1017             if (packetTypeStr == null) throw new ArgumentNullException("packetTypeStr", "Provided packetType string cannot be null.");
1018             if (packetHandlerDelgatePointer == null) throw new ArgumentNullException("packetHandlerDelgatePointer", "Provided PacketHandlerCallBackDelegate<T> cannot be null.");
1019             if (sendReceiveOptions == null) throw new ArgumentNullException("sendReceiveOptions", "Provided SendReceiveOptions cannot be null.");
1020
1021             lock (globalDictAndDelegateLocker)
1022             {
1023
1024                 if (globalIncomingPacketUnwrappers.ContainsKey(packetTypeStr))
1025                 {
1026                     //Make sure if we already have an existing entry that it matches with the provided
1027                     if (!globalIncomingPacketUnwrappers[packetTypeStr].Options.OptionsCompatible(sendReceiveOptions))
1028                         throw new PacketHandlerException("The proivded SendReceiveOptions are not compatible with existing SendReceiveOptions already specified for this packetTypeStr.");
1029                 }
1030                 else
1031                     globalIncomingPacketUnwrappers.Add(packetTypeStr, new PacketTypeUnwrapper(packetTypeStr, sendReceiveOptions));
1032
1033
1034                 //Ad the handler to the list
1035                 if (globalIncomingPacketHandlers.ContainsKey(packetTypeStr))
1036                 {
1037                     //Make sure we avoid duplicates
1038                     PacketTypeHandlerDelegateWrapper<T> toCompareDelegate = new PacketTypeHandlerDelegateWrapper<T>(packetHandlerDelgatePointer);
1039
1040                     bool delegateAlreadyExists = false;
1041                     foreach (var handler in globalIncomingPacketHandlers[packetTypeStr])
1042                     {
1043                         if (handler == toCompareDelegate)
1044                         {
1045                             delegateAlreadyExists = true;
1046                             break;
1047                         }
1048                     }
1049
1050                     if (delegateAlreadyExists)
1051                         throw new PacketHandlerException("This specific packet handler delegate already exists for the provided packetTypeStr.");
1052
1053                     globalIncomingPacketHandlers[packetTypeStr].Add(new PacketTypeHandlerDelegateWrapper<T>(packetHandlerDelgatePointer));
1054                 }
1055                 else
1056                     globalIncomingPacketHandlers.Add(packetTypeStr, new List<IPacketTypeHandlerDelegateWrapper>() { new PacketTypeHandlerDelegateWrapper<T>(packetHandlerDelgatePointer) });
1057
1058                 if (LoggingEnabled) logger.Info("Added incoming packetHandler for ‘" + packetTypeStr + "‘ packetType.");
1059             }
1060         }
1061
1062         /// <summary>
1063         /// Removes the provided delegate for the specified packet type. If the provided delegate does not exist for this packet type just returns.
1064         /// </summary>
1065         /// <param name="packetTypeStr">The packet type for which the delegate will be removed</param>
1066         /// <param name="packetHandlerDelgatePointer">The delegate to be removed</param>
1067         public static void RemoveGlobalIncomingPacketHandler(string packetTypeStr, Delegate packetHandlerDelgatePointer)
1068         {
1069             lock (globalDictAndDelegateLocker)
1070             {
1071                 if (globalIncomingPacketHandlers.ContainsKey(packetTypeStr))
1072                 {
1073                     //Remove any instances of this handler from the delegates
1074                     //The bonus here is if the delegate has not been added we continue quite happily
1075                     IPacketTypeHandlerDelegateWrapper toRemove = null;
1076
1077                     foreach (var handler in globalIncomingPacketHandlers[packetTypeStr])
1078                     {
1079                         if (handler.EqualsDelegate(packetHandlerDelgatePointer))
1080                         {
1081                             toRemove = handler;
1082                             break;
1083                         }
1084                     }
1085
1086                     if (toRemove != null)
1087                         globalIncomingPacketHandlers[packetTypeStr].Remove(toRemove);
1088
1089                     if (globalIncomingPacketHandlers[packetTypeStr] == null || globalIncomingPacketHandlers[packetTypeStr].Count == 0)
1090                     {
1091                         globalIncomingPacketHandlers.Remove(packetTypeStr);
1092                         globalIncomingPacketUnwrappers.Remove(packetTypeStr);
1093
1094                         if (LoggingEnabled) logger.Info("Removed a packetHandler for ‘" + packetTypeStr + "‘ packetType. No handlers remain.");
1095                     }
1096                     else
1097                         if (LoggingEnabled) logger.Info("Removed a packetHandler for ‘" + packetTypeStr + "‘ packetType. Handlers remain.");
1098                 }
1099             }
1100         }
1101
1102         /// <summary>
1103         /// Removes all delegates for the provided packet type.
1104         /// </summary>
1105         /// <param name="packetTypeStr">Packet type for which all delegates should be removed</param>
1106         public static void RemoveGlobalIncomingPacketHandler(string packetTypeStr)
1107         {
1108             lock (globalDictAndDelegateLocker)
1109             {
1110                 //We don‘t need to check for potentially removing a critical reserved packet handler here because those cannot be removed.
1111                 if (globalIncomingPacketHandlers.ContainsKey(packetTypeStr))
1112                 {
1113                     globalIncomingPacketHandlers.Remove(packetTypeStr);
1114                     globalIncomingPacketUnwrappers.Remove(packetTypeStr);
1115
1116                     if (LoggingEnabled) logger.Info("Removed all incoming packetHandlers for ‘" + packetTypeStr + "‘ packetType.");
1117                 }
1118             }
1119         }
1120
1121         /// <summary>
1122         /// Removes all delegates for all packet types
1123         /// </summary>
1124         public static void RemoveGlobalIncomingPacketHandler()
1125         {
1126             lock (globalDictAndDelegateLocker)
1127             {
1128                 globalIncomingPacketHandlers = new Dictionary<string, List<IPacketTypeHandlerDelegateWrapper>>();
1129                 globalIncomingPacketUnwrappers = new Dictionary<string, PacketTypeUnwrapper>();
1130
1131                 if (LoggingEnabled) logger.Info("Removed all incoming packetHandlers for all packetTypes");
1132             }
1133         }
1134
1135         /// <summary>
1136         /// Trigger incoming packet delegates for the provided parameters.
1137         /// </summary>
1138         /// <param name="packetHeader">The packet header</param>
1139         /// <param name="connection">The incoming connection</param>
1140         /// <param name="incomingDataStream">The bytes corresponding to the incoming object</param>
1141         /// <param name="options">The SendReceiveOptions to be used to convert incomingObjectBytes back to the desired object</param>
1142         public static void TriggerGlobalPacketHandlers(PacketHeader packetHeader, Connection connection, MemoryStream incomingDataStream, SendReceiveOptions options)
1143         {
1144             TriggerGlobalPacketHandlers(packetHeader, connection, incomingDataStream, options, IgnoreUnknownPacketTypes);
1145         }
1146
1147         /// <summary>
1148         /// Trigger incoming packet delegates for the provided parameters.
1149         /// </summary>
1150         /// <param name="packetHeader">The packet header</param>
1151         /// <param name="connection">The incoming connection</param>
1152         /// <param name="incomingDataStream">The bytes corresponding to the incoming object</param>
1153         /// <param name="options">The SendReceiveOptions to be used to convert incomingObjectBytes back to the desired object</param>
1154         /// <param name="ignoreUnknownPacketTypeOverride">Used to potentially override NetworkComms.IgnoreUnknownPacketTypes property</param>
1155         internal static void TriggerGlobalPacketHandlers(PacketHeader packetHeader, Connection connection, MemoryStream incomingDataStream, SendReceiveOptions options, bool ignoreUnknownPacketTypeOverride = false)
1156         {
1157             try
1158             {
1159                 if (options == null) throw new PacketHandlerException("Provided sendReceiveOptions should not be null for packetType " + packetHeader.PacketType);
1160
1161                 //We take a copy of the handlers list incase it is modified outside of the lock
1162                 List<IPacketTypeHandlerDelegateWrapper> handlersCopy = null;
1163                 lock (globalDictAndDelegateLocker)
1164                     if (globalIncomingPacketHandlers.ContainsKey(packetHeader.PacketType))
1165                         handlersCopy = new List<IPacketTypeHandlerDelegateWrapper>(globalIncomingPacketHandlers[packetHeader.PacketType]);
1166
1167                 if (handlersCopy == null && !IgnoreUnknownPacketTypes && !ignoreUnknownPacketTypeOverride)
1168                 {
1169                     //We may get here if we have not added any custom delegates for reserved packet types
1170                     bool isReservedType = false;
1171
1172                     for (int i = 0; i < reservedPacketTypeNames.Length; i++)
1173                     {
1174                         if (reservedPacketTypeNames[i] == packetHeader.PacketType)
1175                         {
1176                             isReservedType = true;
1177                             break;
1178                         }
1179                     }
1180
1181                     if (!isReservedType)
1182                     {
1183                         //Change this to just a log because generally a packet of the wrong type is nothing to really worry about
1184                         if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Warn("The received packet type ‘" + packetHeader.PacketType + "‘ has no configured handler and network comms is not set to ignore unknown packet types. Set NetworkComms.IgnoreUnknownPacketTypes=true to prevent this error.");
1185                         LogError(new UnexpectedPacketTypeException("The received packet type ‘" + packetHeader.PacketType + "‘ has no configured handler and network comms is not set to ignore unknown packet types. Set NetworkComms.IgnoreUnknownPacketTypes=true to prevent this error."), "PacketHandlerErrorGlobal_" + packetHeader.PacketType);
1186                     }
1187
1188                     return;
1189                 }
1190                 else if (handlersCopy == null && (IgnoreUnknownPacketTypes || ignoreUnknownPacketTypeOverride))
1191                     //If we have received and unknown packet type and we are choosing to ignore them we just finish here
1192                     return;
1193                 else
1194                 {
1195                     //Idiot check
1196                     if (handlersCopy.Count == 0)
1197                         throw new PacketHandlerException("An entry exists in the packetHandlers list but it contains no elements. This should not be possible.");
1198
1199                     //Deserialise the object only once
1200                     object returnObject = handlersCopy[0].DeSerialize(incomingDataStream, options);
1201
1202                     //Pass the data onto the handler and move on.
1203                     if (LoggingEnabled) logger.Trace(" ... passing completed data packet of type ‘" + packetHeader.PacketType + "‘ to " + handlersCopy.Count.ToString() + " selected global handlers.");
1204
1205                     //Pass the object to all necessary delgates
1206                     //We need to use a copy because we may modify the original delegate list during processing
1207                     foreach (IPacketTypeHandlerDelegateWrapper wrapper in handlersCopy)
1208                     {
1209                         try
1210                         {
1211                             wrapper.Process(packetHeader, connection, returnObject);
1212                         }
1213                         catch (Exception ex)
1214                         {
1215                             if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Fatal("An unhandled exception was caught while processing a packet handler for a packet type ‘" + packetHeader.PacketType + "‘. Make sure to catch errors in packet handlers. See error log file for more information.");
1216                             NetworkComms.LogError(ex, "PacketHandlerErrorGlobal_" + packetHeader.PacketType);
1217                         }
1218                     }
1219
1220                     if (LoggingEnabled) logger.Trace(" ... all handlers for packet of type ‘" + packetHeader.PacketType + "‘ completed.");
1221                 }
1222             }
1223             catch (Exception ex)
1224             {
1225                 //If anything goes wrong here all we can really do is log the exception
1226                 if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Fatal("An exception occured in TriggerPacketHandler() for a packet type ‘" + packetHeader.PacketType + "‘. See error log file for more information.");
1227                 NetworkComms.LogError(ex, "PacketHandlerErrorGlobal_" + packetHeader.PacketType);
1228             }
1229         }
1230
1231         /// <summary>
1232         /// Returns the unwrapper <see cref="SendReceiveOptions"/> for the provided packet type. If no specific options are registered returns null.
1233         /// </summary>
1234         /// <param name="packetTypeStr">The packet type for which the <see cref="SendReceiveOptions"/> are required</param>
1235         /// <returns>The requested <see cref="SendReceiveOptions"/> otherwise null</returns>
1236         public static SendReceiveOptions GlobalPacketTypeUnwrapperOptions(string packetTypeStr)
1237         {
1238             SendReceiveOptions options = null;
1239
1240             //If we find a global packet unwrapper for this packetType we used those options
1241             lock (globalDictAndDelegateLocker)
1242             {
1243                 if (globalIncomingPacketUnwrappers.ContainsKey(packetTypeStr))
1244                     options = globalIncomingPacketUnwrappers[packetTypeStr].Options;
1245             }
1246
1247             return options;
1248         }
1249
1250         /// <summary>
1251         /// Returns true if a global packet handler exists for the provided packet type.
1252         /// </summary>
1253         /// <param name="packetTypeStr">The packet type for which to check incoming packet handlers</param>
1254         /// <returns>True if a global packet handler exists</returns>
1255         public static bool GlobalIncomingPacketHandlerExists(string packetTypeStr)
1256         {
1257             lock (globalDictAndDelegateLocker)
1258                 return globalIncomingPacketHandlers.ContainsKey(packetTypeStr);
1259         }
1260
1261         /// <summary>
1262         /// Returns true if the provided global packet handler has been added for the provided packet type.
1263         /// </summary>
1264         /// <param name="packetTypeStr">The packet type within which to check packet handlers</param>
1265         /// <param name="packetHandlerDelgatePointer">The packet handler to look for</param>
1266         /// <returns>True if a global packet handler exists for the provided packetType</returns>
1267         public static bool GlobalIncomingPacketHandlerExists(string packetTypeStr, Delegate packetHandlerDelgatePointer)
1268         {
1269             lock (globalDictAndDelegateLocker)
1270             {
1271                 if (globalIncomingPacketHandlers.ContainsKey(packetTypeStr))
1272                 {
1273                     foreach (var handler in globalIncomingPacketHandlers[packetTypeStr])
1274                     {
1275                         if (handler.EqualsDelegate(packetHandlerDelgatePointer))
1276                             return true;
1277                     }
1278                 }
1279             }
1280
1281             return false;
1282         }
1283         #endregion
1284
1285         #region Connection Establish and Shutdown
1286         /// <summary>
1287         /// Delegate which is executed when a connection is established or shutdown. See <see cref="AppendGlobalConnectionEstablishHandler"/> and <see cref="AppendGlobalConnectionCloseHandler"/>.
1288         /// </summary>
1289         /// <param name="connection">The connection which has been established or shutdown.</param>
1290         public delegate void ConnectionEstablishShutdownDelegate(Connection connection);
1291
1292         /// <summary>
1293         /// Multicast delegate pointer for connection shutdowns.
1294         /// </summary>
1295         internal static ConnectionEstablishShutdownDelegate globalConnectionShutdownDelegates;
1296
1297         /// <summary>
1298         /// Delegate counter for debugging.
1299         /// </summary>
1300         internal static int globalConnectionShutdownDelegateCount = 0;
1301
1302         /// <summary>
1303         /// Multicast delegate pointer for connection establishments, run asynchronously.
1304         /// </summary>
1305         internal static ConnectionEstablishShutdownDelegate globalConnectionEstablishDelegatesAsync;
1306
1307         /// <summary>
1308         /// Multicast delegate pointer for connection establishments, run synchronously.
1309         /// </summary>
1310         internal static ConnectionEstablishShutdownDelegate globalConnectionEstablishDelegatesSync;
1311
1312         /// <summary>
1313         /// Delegate counter for debugging.
1314         /// </summary>
1315         internal static int globalConnectionEstablishDelegateCount = 0;
1316
1317         /// <summary>
1318         /// Comms shutdown event. This will be triggered when calling NetworkComms.Shutdown
1319         /// </summary>
1320         public static event EventHandler<EventArgs> OnCommsShutdown;
1321
1322         /// <summary>
1323         /// Add a new connection shutdown delegate which will be called for every connection as it is closes.
1324         /// </summary>
1325         /// <param name="connectionShutdownDelegate">The delegate to call on all connection shutdowns</param>
1326         public static void AppendGlobalConnectionCloseHandler(ConnectionEstablishShutdownDelegate connectionShutdownDelegate)
1327         {
1328             lock (globalDictAndDelegateLocker)
1329             {
1330                 if (globalConnectionShutdownDelegates == null)
1331                     globalConnectionShutdownDelegates = connectionShutdownDelegate;
1332                 else
1333                     globalConnectionShutdownDelegates += connectionShutdownDelegate;
1334
1335                 globalConnectionShutdownDelegateCount++;
1336
1337                 if (LoggingEnabled) logger.Info("Added globalConnectionShutdownDelegates. " + globalConnectionShutdownDelegateCount.ToString());
1338             }
1339         }
1340
1341         /// <summary>
1342         /// Remove a connection shutdown delegate.
1343         /// </summary>
1344         /// <param name="connectionShutdownDelegate">The delegate to remove from connection shutdown events</param>
1345         public static void RemoveGlobalConnectionCloseHandler(ConnectionEstablishShutdownDelegate connectionShutdownDelegate)
1346         {
1347             lock (globalDictAndDelegateLocker)
1348             {
1349                 globalConnectionShutdownDelegates -= connectionShutdownDelegate;
1350                 globalConnectionShutdownDelegateCount--;
1351
1352                 if (LoggingEnabled) logger.Info("Removed globalConnectionShutdownDelegates. " + globalConnectionShutdownDelegateCount.ToString());
1353             }
1354         }
1355
1356         /// <summary>
1357         /// Add a new connection establish delegate which will be called for every connection once it has been succesfully established.
1358         /// </summary>
1359         /// <param name="connectionEstablishDelegate">The delegate to call after all connection establishments.</param>
1360         /// <param name="runSynchronously">If true this ConnectionEstablishShutdownDelegate will be called synchronously during the connection establish. The connection will not be considered established until the ConnectionEstablishShutdownDelegate has completed.</param>
1361         public static void AppendGlobalConnectionEstablishHandler(ConnectionEstablishShutdownDelegate connectionEstablishDelegate, bool runSynchronously = false)
1362         {
1363             lock (globalDictAndDelegateLocker)
1364             {
1365                 if (runSynchronously)
1366                 {
1367                     if (globalConnectionEstablishDelegatesSync == null)
1368                         globalConnectionEstablishDelegatesSync = connectionEstablishDelegate;
1369                     else
1370                         globalConnectionEstablishDelegatesSync += connectionEstablishDelegate;
1371                 }
1372                 else
1373                 {
1374                     if (globalConnectionEstablishDelegatesAsync == null)
1375                         globalConnectionEstablishDelegatesAsync = connectionEstablishDelegate;
1376                     else
1377                         globalConnectionEstablishDelegatesAsync += connectionEstablishDelegate;
1378                 }
1379
1380                 globalConnectionEstablishDelegateCount++;
1381
1382                 if (LoggingEnabled) logger.Info("Added globalConnectionEstablishDelegates. " + globalConnectionEstablishDelegateCount.ToString());
1383             }
1384         }
1385
1386         /// <summary>
1387         /// Remove a connection establish delegate.
1388         /// </summary>
1389         /// <param name="connectionEstablishDelegate">The delegate to remove from connection establish events</param>
1390         public static void RemoveGlobalConnectionEstablishHandler(ConnectionEstablishShutdownDelegate connectionEstablishDelegate)
1391         {
1392             lock (globalDictAndDelegateLocker)
1393             {
1394                 //Remove from either async or sync delegates
1395                 globalConnectionEstablishDelegatesAsync -= connectionEstablishDelegate;
1396                 globalConnectionEstablishDelegatesSync -= connectionEstablishDelegate;
1397
1398                 globalConnectionEstablishDelegateCount--;
1399
1400                 if (LoggingEnabled) logger.Info("Removed globalConnectionEstablishDelegates. " + globalConnectionEstablishDelegateCount.ToString());
1401             }
1402         }
1403
1404         /// <summary>
1405         /// Shutdown all connections, comms threads and execute OnCommsShutdown event. Any packet handlers are left unchanged. If any comms activity has taken place this should be called on application close.
1406         /// </summary>
1407         /// <param name="threadShutdownTimeoutMS">The time to wait for worker threads to close before attempting a thread abort.</param>
1408         public static void Shutdown(int threadShutdownTimeoutMS = 1000)
1409         {
1410             if (LoggingEnabled) logger.Trace("NetworkCommsDotNet shutdown initiated.");
1411             commsShutdown = true;
1412
1413             CommsThreadPool.BeginShutdown();
1414             Connection.ShutdownBase(threadShutdownTimeoutMS);
1415             TCPConnection.Shutdown(threadShutdownTimeoutMS);
1416             UDPConnection.Shutdown();
1417
1418             try
1419             {
1420                 CloseAllConnections();
1421             }
1422             catch (CommsException)
1423             {
1424
1425             }
1426             catch (Exception ex)
1427             {
1428                 LogError(ex, "CommsShutdownError");
1429             }
1430
1431 #if !WINDOWS_PHONE && !ANDROID
1432             try
1433             {
1434                 if (NetworkLoadThread != null)
1435                 {
1436                     NetworkLoadThreadWait.Set();
1437                     if (!NetworkLoadThread.Join(threadShutdownTimeoutMS))
1438                     {
1439                         NetworkLoadThread.Abort();
1440                         throw new CommsSetupShutdownException("Timeout waiting for NetworkLoadThread thread to shutdown after " + threadShutdownTimeoutMS.ToString() + " ms. ");
1441                     }
1442                 }
1443             }
1444             catch (Exception ex)
1445             {
1446                 LogError(ex, "CommsShutdownError");
1447             }
1448 #endif
1449
1450             try
1451             {
1452                 if (OnCommsShutdown != null) OnCommsShutdown(null, new EventArgs());
1453             }
1454             catch (Exception ex)
1455             {
1456                 LogError(ex, "CommsShutdownError");
1457             }
1458
1459             CommsThreadPool.EndShutdown(threadShutdownTimeoutMS);
1460
1461             commsShutdown = false;
1462             if (LoggingEnabled) logger.Info("NetworkCommsDotNet has shutdown");
1463
1464 #if !WINDOWS_PHONE && !NO_LOGGING
1465             //Mono bug fix
1466             //Sometimes NLog ends up in a deadlock on close, workaround provided on NLog website
1467             if (Logger != null)
1468             {
1469                 LogManager.Flush();
1470                 Logger.Factory.Flush();
1471
1472                 if (NetworkComms.CurrentRuntimeEnvironment == RuntimeEnvironment.Mono_Net2 ||
1473                     NetworkComms.CurrentRuntimeEnvironment == RuntimeEnvironment.Mono_Net35 ||
1474                     NetworkComms.CurrentRuntimeEnvironment == RuntimeEnvironment.Mono_Net4)
1475                     LogManager.Configuration = null;
1476             }
1477 #endif
1478         }
1479         #endregion
1480
1481         #region Timeouts
1482         /// <summary>
1483         /// Time to wait in milliseconds before throwing an exception when waiting for a connection to be established. Default is 30000.
1484         /// </summary>
1485         public static int ConnectionEstablishTimeoutMS { get; set; }
1486
1487         /// <summary>
1488         /// Time to wait in milliseconds before throwing an exception when waiting for confirmation of packet receipt. Default is 5000.
1489         /// </summary>
1490         public static int PacketConfirmationTimeoutMS { get; set; }
1491
1492         /// <summary>
1493         /// Time to wait in milliseconds before assuming a remote connection is dead when doing a connection test. Default is 1000.
1494         /// </summary>
1495         public static int ConnectionAliveTestTimeoutMS { get; set; }
1496
1497         /// <summary>
1498         /// By default NetworkComms.Net closes connections for which sends take a long time. The timeout is calculated based on previous connection send performances. Set this to true to disable this feature.
1499         /// </summary>
1500         public static bool DisableConnectionSendTimeouts { get; set; }
1501         #endregion
1502
1503         #region Logging
1504         /// <summary>
1505         /// Returns true if comms logging has been enabled.
1506         /// </summary>
1507         public static bool LoggingEnabled { get; private set; }
1508
1509         private static Logger logger = null;
1510
1511         /// <summary>
1512         /// Access the NetworkCommsDotNet logger externally.
1513         /// </summary>
1514         public static Logger Logger
1515         {
1516             get { return logger; }
1517         }
1518
1519 #if NO_LOGGING
1520         /// <summary>
1521         /// Enable basic logging using the provided logFileLocation
1522         /// </summary>
1523         /// <param name="loggingConfiguration"></param>
1524         public static void EnableLogging(string logFileLocation)
1525         {
1526             lock (globalDictAndDelegateLocker)
1527             {
1528                 LoggingEnabled = true;
1529                 logger = new Logger();
1530                 logger.LogFileLocation = logFileLocation;
1531             }
1532         }
1533
1534         /// <summary>
1535         /// Disable all logging in NetworkCommsDotNet
1536         /// </summary>
1537         public static void DisableLogging()
1538         {
1539             lock (globalDictAndDelegateLocker)
1540             {
1541                 LoggingEnabled = false;
1542                 logger = null;
1543             }
1544         }
1545 #else
1546         /// <summary>
1547         /// Enable logging using a default config. All log output is written directly to the local console.
1548         /// </summary>
1549         public static void EnableLogging()
1550         {
1551             LoggingConfiguration logConfig = new LoggingConfiguration();
1552             NLog.Targets.ConsoleTarget consoleTarget = new NLog.Targets.ConsoleTarget();
1553             consoleTarget.Layout = "${date:format=HH\\:MM\\:ss} [${level}] - ${message}";
1554             logConfig.AddTarget("console", consoleTarget);
1555             logConfig.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, consoleTarget));
1556             EnableLogging(logConfig);
1557         }
1558
1559         /// <summary>
1560         /// Enable logging using the provided config. See examples for usage.
1561         /// </summary>
1562         /// <param name="loggingConfiguration"></param>
1563         public static void EnableLogging(LoggingConfiguration loggingConfiguration)
1564         {
1565             lock (globalDictAndDelegateLocker)
1566             {
1567                 LoggingEnabled = true;
1568                 LogManager.Configuration = loggingConfiguration;
1569                 logger = LogManager.GetCurrentClassLogger();
1570                 LogManager.EnableLogging();
1571             }
1572         }
1573
1574         /// <summary>
1575         /// Disable all logging in NetworkCommsDotNet
1576         /// </summary>
1577         public static void DisableLogging()
1578         {
1579             lock (globalDictAndDelegateLocker)
1580             {
1581                 LoggingEnabled = false;
1582                 LogManager.DisableLogging();
1583             }
1584         }
1585 #endif
1586
1587         /// <summary>
1588         /// Locker for LogError() which ensures thread safe saves.
1589         /// </summary>
1590         static object errorLocker = new object();
1591
1592         /// <summary>
1593         /// Appends the provided logString to end of fileName.txt. If the file does not exist it will be created.
1594         /// </summary>
1595         /// <param name="fileName">The filename to use. The extension .txt will be appended automatically</param>
1596         /// <param name="logString">The string to append.</param>
1597         public static void AppendStringToLogFile(string fileName, string logString)
1598         {
1599             try
1600             {
1601                 lock (errorLocker)
1602                 {
1603                     using (System.IO.StreamWriter sw = new System.IO.StreamWriter(fileName + ".txt", true))
1604                         sw.WriteLine(logString);
1605                 }
1606             }
1607             catch (Exception)
1608             {
1609                 //If an error happens here, such as if the file is locked then we lucked out.
1610             }
1611         }
1612
1613         /// <summary>
1614         /// Logs the provided exception to a file to assist troubleshooting.
1615         /// </summary>
1616         /// <param name="ex">The exception to be logged</param>
1617         /// <param name="fileName">The filename to use. A timestamp and extension .txt will be appended automatically</param>
1618         /// <param name="optionalCommentStr">An optional string which will appear at the top of the error file</param>
1619         /// <returns>The entire fileName used.</returns>
1620         public static string LogError(Exception ex, string fileName, string optionalCommentStr = "")
1621         {
1622             string entireFileName;
1623
1624             lock (errorLocker)
1625             {
1626
1627 #if iOS
1628                 //We need to ensure we add the correct document path for iOS
1629                 entireFileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), fileName + " " + DateTime.Now.Hour.ToString() + "." + DateTime.Now.Minute.ToString() + "." + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + " " + DateTime.Now.ToString("dd-MM-yyyy" + " [" + Thread.CurrentThread.ManagedThreadId.ToString() + "]"));
1630 #elif ANDROID
1631                 entireFileName = Path.Combine(global::Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, fileName + " " + DateTime.Now.Hour.ToString() + "." + DateTime.Now.Minute.ToString() + "." + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + " " + DateTime.Now.ToString("dd-MM-yyyy" + " [" + Thread.CurrentThread.ManagedThreadId.ToString() + "]"));
1632 #elif WINDOWS_PHONE
1633                 entireFileName = fileName + " " + DateTime.Now.Hour.ToString() + "." + DateTime.Now.Minute.ToString() + "." + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + " " + DateTime.Now.ToString("dd-MM-yyyy" + " [" + Thread.CurrentThread.ManagedThreadId.ToString() + "]");
1634 #else
1635                 using (Process currentProcess = System.Diagnostics.Process.GetCurrentProcess())
1636                     entireFileName = fileName + " " + DateTime.Now.Hour.ToString() + "." + DateTime.Now.Minute.ToString() + "." + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + " " + DateTime.Now.ToString("dd-MM-yyyy" + " [" + currentProcess.Id.ToString() + "-" + Thread.CurrentContext.ContextID.ToString() + "]");
1637 #endif
1638
1639                 if (LoggingEnabled) logger.Fatal(entireFileName, ex);
1640
1641                 try
1642                 {
1643                     using (System.IO.StreamWriter sw = new System.IO.StreamWriter(entireFileName + ".txt", false))
1644                     {
1645                         if (optionalCommentStr != "")
1646                         {
1647                             sw.WriteLine("Comment: " + optionalCommentStr);
1648                             sw.WriteLine("");
1649                         }
1650
1651                         if (ex.GetBaseException() != null)
1652                             sw.WriteLine("Base Exception Type: " + ex.GetBaseException().ToString());
1653
1654                         if (ex.InnerException != null)
1655                             sw.WriteLine("Inner Exception Type: " + ex.InnerException.ToString());
1656
1657                         if (ex.StackTrace != null)
1658                         {
1659                             sw.WriteLine("");
1660                             sw.WriteLine("Stack Trace: " + ex.StackTrace.ToString());
1661                         }
1662                     }
1663                 }
1664                 catch (Exception)
1665                 {
1666                     //This should never really happen, but just incase.
1667                 }
1668             }
1669
1670             return entireFileName;
1671         }
1672         #endregion
1673
1674         #region Serializers and Compressors
1675
1676         /// <summary>
1677         /// The following are used for internal comms objects, packet headers, connection establishment etc.
1678         /// We generally seem to increase the size of our data if compressing small objects (~50 bytes)
1679         /// Given the typical header size is 40 bytes we might as well not compress these objects.
1680         /// </summary>
1681         internal static SendReceiveOptions InternalFixedSendReceiveOptions { get; set; }
1682
1683         /// <summary>
1684         /// Default options for sending and receiving in the absence of specific values
1685         /// </summary>
1686         public static SendReceiveOptions DefaultSendReceiveOptions { get; set; }
1687         #endregion
1688
1689         #region Connection Access
1690         /// <summary>
1691         /// Send the provided object to the specified destination using TCP. Uses default sendReceiveOptions. For more control over options see connection specific methods.
1692         /// </summary>
1693         /// <param name="packetTypeStr">Packet type to use for send</param>
1694         /// <param name="destinationIPAddress">The destination ip address</param>
1695         /// <param name="destinationPort">The destination listen port</param>
1696         /// <param name="sendObject">The obect to send</param>
1697         public static void SendObject(string packetTypeStr, string destinationIPAddress, int destinationPort, object sendObject)
1698         {
1699             TCPConnection conn = TCPConnection.GetConnection(new ConnectionInfo(destinationIPAddress, destinationPort));
1700             conn.SendObject(packetTypeStr, sendObject);
1701         }
1702
1703         /// <summary>
1704         /// Send the provided object to the specified destination and wait for a return object using TCP. Uses default sendReceiveOptions. For more control over options see connection specific methods.
1705         /// </summary>
1706         /// <typeparam name="returnObjectType">The expected return object type, i.e. string, int[], etc</typeparam>
1707         /// <param name="sendingPacketTypeStr">Packet type to use during send</param>
1708         /// <param name="destinationIPAddress">The destination ip address</param>
1709         /// <param name="destinationPort">The destination listen port</param>
1710         /// <param name="expectedReturnPacketTypeStr">Expected packet type used for return object</param>
1711         /// <param name="returnPacketTimeOutMilliSeconds">Time to wait in milliseconds for return object</param>
1712         /// <param name="sendObject">Object to send</param>
1713         /// <returns>The expected return object</returns>
1714         public static returnObjectType SendReceiveObject<returnObjectType>(string sendingPacketTypeStr, string destinationIPAddress, int destinationPort, string expectedReturnPacketTypeStr, int returnPacketTimeOutMilliSeconds, object sendObject)
1715         {
1716             TCPConnection conn = TCPConnection.GetConnection(new ConnectionInfo(destinationIPAddress, destinationPort));
1717             return conn.SendReceiveObject<returnObjectType>(sendingPacketTypeStr, expectedReturnPacketTypeStr, returnPacketTimeOutMilliSeconds, sendObject);
1718         }
1719
1720         /// <summary>
1721         /// Return the MD5 hash of the provided memory stream as a string. Stream position will be equal to the length of stream on return, this ensures the MD5 is consistent.
1722         /// </summary>
1723         /// <param name="streamToMD5">The bytes which will be checksummed</param>
1724         /// <returns>The MD5 checksum as a string</returns>
1725         public static string MD5Bytes(Stream streamToMD5)
1726         {
1727             if (streamToMD5 == null) throw new ArgumentNullException("streamToMD5", "Provided Stream cannot be null.");
1728
1729             string resultStr;
1730
1731             using (System.Security.Cryptography.HashAlgorithm md5 =
1732 #if WINDOWS_PHONE
1733             new DPSBase.MD5Managed())
1734 #else
1735             System.Security.Cryptography.MD5.Create())
1736 #endif
1737             {
1738                 //If we don‘t ensure the position is consistent the MD5 changes
1739                 streamToMD5.Seek(0, SeekOrigin.Begin);
1740                 resultStr = BitConverter.ToString(md5.ComputeHash(streamToMD5)).Replace("-", "");
1741             }
1742
1743             return resultStr;
1744         }
1745
1746         /// <summary>
1747         /// Return the MD5 hash of the provided memory stream as a string. Stream position will be equal to the length of stream on return, this ensures the MD5 is consistent.
1748         /// </summary>
1749         /// <param name="streamToMD5">The bytes which will be checksummed</param>
1750         /// <param name="start">The start position in the stream</param>
1751         /// <param name="length">The length in the stream to MD5</param>
1752         /// <returns>The MD5 checksum as a string</returns>
1753         public static string MD5Bytes(Stream streamToMD5, long start, int length)
1754         {
1755             if (streamToMD5 == null) throw new ArgumentNullException("streamToMD5", "Provided Stream cannot be null.");
1756
1757             using (MemoryStream stream = new MemoryStream(length))
1758             {
1759                 StreamWriteWithTimeout.Write(streamToMD5, start, length, stream, 8000, 100, 2000);
1760                 return MD5Bytes(stream);
1761             }
1762         }
1763
1764         /// <summary>
1765         /// Return the MD5 hash of the provided byte array as a string
1766         /// </summary>
1767         /// <param name="bytesToMd5">The bytes which will be checksummed</param>
1768         /// <returns>The MD5 checksum as a string</returns>
1769         public static string MD5Bytes(byte[] bytesToMd5)
1770         {
1771             if (bytesToMd5 == null) throw new ArgumentNullException("bytesToMd5", "Provided byte[] cannot be null.");
1772
1773             using(MemoryStream stream = new MemoryStream(bytesToMd5, 0, bytesToMd5.Length, false, true))
1774                 return MD5Bytes(stream);
1775         }
1776
1777         /// <summary>
1778         /// Returns a ConnectionInfo array containing information for all connections
1779         /// </summary>
1780         /// <param name="includeClosedConnections">If true information for closed connections will also be included</param>
1781         /// <returns>List of ConnectionInfo containing information for all requested connections</returns>
1782         public static List<ConnectionInfo> AllConnectionInfo(bool includeClosedConnections = false)
1783         {
1784             List<ConnectionInfo> returnList = new List<ConnectionInfo>();
1785
1786             lock (globalDictAndDelegateLocker)
1787             {
1788                 foreach (var connectionsByEndPoint in allConnectionsByEndPoint)
1789                 {
1790                     foreach (var connection in connectionsByEndPoint.Value.Values)
1791                     {
1792                         if (connection.ConnectionInfo != null)
1793                             returnList.Add(connection.ConnectionInfo);
1794                     }
1795                 }
1796
1797                 if (includeClosedConnections)
1798                 {
1799                     foreach (var pair in oldNetworkIdentifierToConnectionInfo)
1800                     {
1801                         foreach (var infoList in pair.Value.Values)
1802                         {
1803                             returnList.AddRange(infoList);
1804                         }
1805                     }
1806                 }
1807             }
1808
1809             List<ConnectionInfo> distinctList = new List<ConnectionInfo>();
1810             foreach (var info in returnList)
1811                 if (!distinctList.Contains(info))
1812                     distinctList.Add(info);
1813
1814             return distinctList;
1815         }
1816
1817         /// <summary>
1818         /// Returns a ConnectionInfo array containing information for all connections which have the provided networkIdentifier. It is also possible to include information for closed connections.
1819         /// </summary>
1820         /// <param name="networkIdentifier">The networkIdentifier corresponding to the desired connectionInfo information</param>
1821         /// <param name="includeClosedConnections">If true will include information for connections which are closed. Otherwise only active connections will be included.</param>
1822         /// <returns>List of ConnectionInfo containing information for matching connections</returns>
1823         public static List<ConnectionInfo> AllConnectionInfo(ShortGuid networkIdentifier, bool includeClosedConnections = false)
1824         {
1825             List<ConnectionInfo> returnList = new List<ConnectionInfo>();
1826
1827             lock (globalDictAndDelegateLocker)
1828             {
1829                 foreach (var pair in allConnectionsByEndPoint)
1830                 {
1831                     foreach (var connection in pair.Value.Values)
1832                     {
1833                         if (connection.ConnectionInfo != null && connection.ConnectionInfo.NetworkIdentifier == networkIdentifier)
1834                                 returnList.Add(connection.ConnectionInfo);
1835                     }
1836                 }
1837
1838                 if (includeClosedConnections)
1839                 {
1840                     foreach (var pair in oldNetworkIdentifierToConnectionInfo)
1841                     {
1842                         if (pair.Key == networkIdentifier)
1843                         {
1844                             foreach (var infoList in pair.Value.Values)
1845                                 foreach (var info in infoList)
1846                                         returnList.Add(info);
1847
1848                             break;
1849                         }
1850                     }
1851                 }
1852             }
1853
1854             List<ConnectionInfo> distinctList = new List<ConnectionInfo>();
1855             foreach (var info in returnList)
1856                 if (!distinctList.Contains(info))
1857                     distinctList.Add(info);
1858
1859             return distinctList;
1860         }
1861
1862         /// <summary>
1863         /// Returns the total number of connections
1864         /// </summary>
1865         /// <returns>Total number of connections</returns>
1866         public static int TotalNumConnections()
1867         {
1868             lock (globalDictAndDelegateLocker)
1869             {
1870                 int sum = 0;
1871
1872                 foreach (var current in allConnectionsByEndPoint)
1873                     sum += current.Value.Count;
1874
1875                 return sum;
1876             }
1877         }
1878
1879         /// <summary>
1880         /// Returns the total number of connections where the <see cref="ConnectionInfo.RemoteEndPoint"/> matches the provided <see cref="IPAddress"/>
1881         /// </summary>
1882         /// <param name="matchIP">The <see cref="IPAddress"/> to match</param>
1883         /// <returns>Total number of connections where the <see cref="ConnectionInfo.RemoteEndPoint "/> matches the provided <see cref="IPAddress"/></returns>
1884         public static int TotalNumConnections(IPAddress matchIP)
1885         {
1886             lock (globalDictAndDelegateLocker)
1887             {
1888                 int sum = 0;
1889
1890                 foreach (var current in allConnectionsByEndPoint)
1891                     foreach (var connection in current.Value)
1892                         if (connection.Value.ConnectionInfo.RemoteEndPoint.Address.Equals(matchIP))
1893                             sum++;
1894
1895                 return sum;
1896             }
1897         }
1898
1899         /// <summary>
1900         /// Close all connections
1901         /// </summary>
1902         public static void CloseAllConnections()
1903         {
1904             CloseAllConnections(ConnectionType.Undefined, new IPEndPoint[0]);
1905         }
1906
1907         /// <summary>
1908         /// Close all connections of the provided <see cref="ConnectionType"/>
1909         /// </summary>
1910         /// <param name="connectionType">The type of connections to be closed</param>
1911         public static void CloseAllConnections(ConnectionType connectionType)
1912         {
1913             CloseAllConnections(connectionType, new IPEndPoint[0]);
1914         }
1915
1916         /// <summary>
1917         /// Close all connections of the provided <see cref="ConnectionType"/> except to provided <see cref="IPEndPoint"/> array.
1918         /// </summary>
1919         /// <param name="connectionTypeToClose">The type of connections to be closed. ConnectionType.<see cref="ConnectionType.Undefined"/> matches all types.</param>
1920         /// <param name="closeAllExceptTheseEndPoints">Close all except those with provided <see cref="IPEndPoint"/> array</param>
1921         public static void CloseAllConnections(ConnectionType connectionTypeToClose, IPEndPoint[] closeAllExceptTheseEndPoints)
1922         {
1923             List<Connection> connectionsToClose = new List<Connection>();
1924
1925             lock (globalDictAndDelegateLocker)
1926             {
1927                 foreach (var pair in allConnectionsByEndPoint)
1928                 {
1929                     foreach (var innerPair in pair.Value)
1930                     {
1931                         if (innerPair.Value != null && (connectionTypeToClose == ConnectionType.Undefined || innerPair.Key == connectionTypeToClose))
1932                         {
1933                             bool dontClose = false;
1934
1935                             foreach (var endPointToNotClose in closeAllExceptTheseEndPoints)
1936                             {
1937                                 if (endPointToNotClose == innerPair.Value.ConnectionInfo.RemoteEndPoint)
1938                                 {
1939                                     dontClose = true;
1940                                     break;
1941                                 }
1942                             }
1943
1944                             if (!dontClose )
1945                                 connectionsToClose.Add(innerPair.Value);
1946                         }
1947                     }
1948                 }
1949             }
1950
1951             if (LoggingEnabled) logger.Trace("Closing " + connectionsToClose.Count.ToString() + " connections.");
1952
1953             foreach (Connection connection in connectionsToClose)
1954                 connection.CloseConnection(false, -6);
1955         }
1956
1957         /// <summary>
1958         /// Returns a list of all connections
1959         /// </summary>
1960         /// <returns>A list of requested connections. If no matching connections exist returns empty list.</returns>
1961         public static List<Connection> GetExistingConnection()
1962         {
1963             return GetExistingConnection(ConnectionType.Undefined);
1964         }
1965
1966         /// <summary>
1967         /// Returns a list of all connections matching the provided <see cref="ConnectionType"/>
1968         /// </summary>
1969         /// <param name="connectionType">The type of connections to return. ConnectionType.<see cref="ConnectionType.Undefined"/> matches all types.</param>
1970         /// <returns>A list of requested connections. If no matching connections exist returns empty list.</returns>
1971         public static List<Connection> GetExistingConnection(ConnectionType connectionType)
1972         {
1973             List<Connection> result = new List<Connection>();
1974             lock (globalDictAndDelegateLocker)
1975             {
1976                 foreach (var current in allConnectionsByEndPoint)
1977                 {
1978                     foreach (var inner in current.Value)
1979                     {
1980                         if (connectionType == ConnectionType.Undefined || inner.Key == connectionType)
1981                             result.Add(inner.Value);
1982                     }
1983                 }
1984             }
1985
1986             if (LoggingEnabled) logger.Trace("RetrieveConnection by connectionType=‘" + connectionType.ToString() + "‘. Returning list of " + result.Count.ToString() + " connections.");
1987
1988             return result;
1989         }
1990
1991         /// <summary>
1992         /// Retrieve a list of connections with the provided <see cref="ShortGuid"/> networkIdentifier of the provided <see cref="ConnectionType"/>.
1993         /// </summary>
1994         /// <param name="networkIdentifier">The <see cref="ShortGuid"/> corresponding with the desired peer networkIdentifier</param>
1995         /// <param name="connectionType">The <see cref="ConnectionType"/> desired</param>
1996         /// <returns>A list of connections to the desired peer. If no matching connections exist returns empty list.</returns>
1997         public static List<Connection> GetExistingConnection(ShortGuid networkIdentifier, ConnectionType connectionType)
1998         {
1999             List<Connection> resultList = new List<Connection>();
2000             lock (globalDictAndDelegateLocker)
2001             {
2002                 foreach (var pair in allConnectionsById)
2003                 {
2004                     if (pair.Key == networkIdentifier && pair.Value.ContainsKey(connectionType))
2005                     {
2006                         resultList.AddRange(pair.Value[connectionType]);
2007                         break;
2008                     }
2009                 }
2010             }
2011
2012             if (LoggingEnabled) logger.Trace("RetrieveConnection by networkIdentifier=‘" + networkIdentifier + "‘ and connectionType=‘" + connectionType.ToString() + "‘. Returning list of " + resultList.Count.ToString() + " connections.");
2013
2014             return resultList;
2015         }
2016
2017         /// <summary>
2018         /// Retrieve an existing connection with the provided ConnectionInfo.
2019         /// </summary>
2020         /// <param name="connectionInfo">ConnectionInfo corresponding with the desired connection</param>
2021         /// <returns>The desired connection. If no matching connection exists returns null.</returns>
2022         public static Connection GetExistingConnection(ConnectionInfo connectionInfo)
2023         {
2024             if (connectionInfo == null) throw new ArgumentNullException("connectionInfo", "Provided ConnectionInfo cannot be null.");
2025
2026             Connection result = null;
2027             lock (globalDictAndDelegateLocker)
2028             {
2029                 foreach (var pair in allConnectionsByEndPoint)
2030                 {
2031                     if(pair.Key.Equals(connectionInfo.RemoteEndPoint) && pair.Value.ContainsKey(connectionInfo.ConnectionType))
2032                     {
2033                         result = pair.Value[connectionInfo.ConnectionType];
2034                         break;
2035                     }
2036                 }
2037             }
2038
2039             if (LoggingEnabled)
2040             {
2041                 if (result == null)
2042                     logger.Trace("RetrieveConnection by connectionInfo=‘"+connectionInfo+"‘. No matching connection was found.");
2043                 else
2044                     logger.Trace("RetrieveConnection by connectionInfo=‘"+connectionInfo+"‘. Matching connection was found.");
2045             }
2046
2047             return result;
2048         }
2049
2050         /// <summary>
2051         /// Retrieve an existing connection with the provided <see cref="IPEndPoint"/> of the provided <see cref="ConnectionType"/>.
2052         /// </summary>
2053         /// <param name="remoteEndPoint">IPEndPoint corresponding with the desired connection</param>
2054         /// <param name="connectionType">The <see cref="ConnectionType"/> desired</param>
2055         /// <returns>The desired connection. If no matching connection exists returns null.</returns>
2056         public static Connection GetExistingConnection(IPEndPoint remoteEndPoint, ConnectionType connectionType)
2057         {
2058             Connection result = null;
2059             lock (globalDictAndDelegateLocker)
2060             {
2061                 //return (from current in NetworkComms.allConnectionsByEndPoint where current.Key == IPEndPoint && current.Value.ContainsKey(connectionType) select current.Value[connectionType]).FirstOrDefault();
2062                 //return (from current in NetworkComms.allConnectionsByEndPoint where current.Key == IPEndPoint select current.Value[connectionType]).FirstOrDefault();
2063                 if (allConnectionsByEndPoint.ContainsKey(remoteEndPoint))
2064                 {
2065                     if (allConnectionsByEndPoint[remoteEndPoint].ContainsKey(connectionType))
2066                         result = allConnectionsByEndPoint[remoteEndPoint][connectionType];
2067                 }
2068             }
2069
2070             if (LoggingEnabled)
2071             {
2072                 string connectionTypeStr = connectionType.ToString();
2073                 if (result == null)
2074                     logger.Trace("RetrieveConnection by remoteEndPoint=‘" + remoteEndPoint.Address + ":" + remoteEndPoint.Port.ToString() + "‘ and connectionType=‘" + connectionTypeStr + "‘. No matching connection was found.");
2075                 else
2076                     logger.Trace("RetrieveConnection by remoteEndPoint=‘" + remoteEndPoint.Address + ":" + remoteEndPoint.Port.ToString() + "‘ and connectionType=‘" + connectionTypeStr + "‘. Matching connection was found.");
2077             }
2078
2079             return result;
2080         }
2081
2082         /// <summary>
2083         /// Check if a connection exists with the provided IPEndPoint and ConnectionType
2084         /// </summary>
2085         /// <param name="connectionInfo">ConnectionInfo corresponding with the desired connection</param>
2086         /// <returns>True if a matching connection exists, otherwise false</returns>
2087         public static bool ConnectionExists(ConnectionInfo connectionInfo)
2088         {
2089             if (connectionInfo == null) throw new ArgumentNullException("connectionInfo", "Provided ConnectionInfo cannot be null.");
2090
2091             bool result = false;
2092             lock (globalDictAndDelegateLocker)
2093             {
2094                 if (allConnectionsByEndPoint.ContainsKey(connectionInfo.RemoteEndPoint))
2095                     result = allConnectionsByEndPoint[connectionInfo.RemoteEndPoint].ContainsKey(connectionInfo.ConnectionType);
2096             }
2097
2098             if (LoggingEnabled) logger.Trace("Checking for existing connection by connectionInfo=‘" + connectionInfo +"‘");
2099             return result;
2100         }
2101
2102         /// <summary>
2103         /// Check if a connection exists with the provided networkIdentifier and ConnectionType
2104         /// </summary>
2105         /// <param name="networkIdentifier">The <see cref="ShortGuid"/> corresponding with the desired peer networkIdentifier</param>
2106         /// <param name="connectionType">The <see cref="ConnectionType"/> desired</param>
2107         /// <returns>True if a matching connection exists, otherwise false</returns>
2108         public static bool ConnectionExists(ShortGuid networkIdentifier, ConnectionType connectionType)
2109         {
2110             bool result = false;
2111             lock (globalDictAndDelegateLocker)
2112             {
2113                 if (allConnectionsById.ContainsKey(networkIdentifier))
2114                 {
2115                     if (allConnectionsById[networkIdentifier].ContainsKey(connectionType))
2116                         result = allConnectionsById[networkIdentifier][connectionType].Count > 0;
2117                 }
2118             }
2119
2120             if (LoggingEnabled)
2121             {
2122                 string connectionTypeStr = connectionType.ToString();
2123                 logger.Trace("Checking for existing connection by identifier=‘" + networkIdentifier + "‘ and connectionType=‘" + connectionTypeStr + "‘");
2124             }
2125             return result;
2126         }
2127
2128         /// <summary>
2129         /// Check if a connection exists with the provided IPEndPoint and ConnectionType
2130         /// </summary>
2131         /// <param name="remoteEndPoint">IPEndPoint corresponding with the desired connection</param>
2132         /// <param name="connectionType">The <see cref="ConnectionType"/> desired</param>
2133         /// <returns>True if a matching connection exists, otherwise false</returns>
2134         public static bool ConnectionExists(IPEndPoint remoteEndPoint, ConnectionType connectionType)
2135         {
2136             bool result = false;
2137             lock (globalDictAndDelegateLocker)
2138             {
2139                 if (allConnectionsByEndPoint.ContainsKey(remoteEndPoint))
2140                     result = allConnectionsByEndPoint[remoteEndPoint].ContainsKey(connectionType);
2141             }
2142
2143             if (LoggingEnabled)
2144             {
2145                 string connectionTypeStr = connectionType.ToString();
2146                 logger.Trace("Checking for existing connection by endPoint=‘" + remoteEndPoint.Address + ":" + remoteEndPoint.Port.ToString() + "‘ and connectionType=‘" + connectionTypeStr + "‘");
2147             }
2148             return result;
2149         }
2150
2151         /// <summary>
2152         /// Removes the reference to the provided connection from within networkComms. DOES NOT CLOSE THE CONNECTION. Returns true if the provided connection reference existed and was removed, false otherwise.
2153         /// </summary>
2154         /// <param name="connection"></param>
2155         /// <param name="maintainConnectionInfoHistory"></param>
2156         /// <returns></returns>
2157         internal static bool RemoveConnectionReference(Connection connection, bool maintainConnectionInfoHistory = true)
2158         {
2159             if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Entering RemoveConnectionReference for " + connection.ConnectionInfo);
2160
2161             //We don‘t have the connection identifier until the connection has been established.
2162             //if (!connection.ConnectionInfo.ConnectionEstablished && !connection.ConnectionInfo.ConnectionShutdown)
2163             //    return false;
2164
2165             if (connection.ConnectionInfo.ConnectionState == ConnectionState.Established && !(connection.ConnectionInfo.ConnectionState == ConnectionState.Shutdown))
2166                 throw new ConnectionShutdownException("A connection can only be removed once correctly shutdown.");
2167
2168             bool returnValue = false;
2169
2170             //Ensure connection references are removed from networkComms
2171             //Once we think we have closed the connection it‘s time to get rid of our other references
2172             lock (globalDictAndDelegateLocker)
2173             {
2174                 #region Update NetworkComms Connection Dictionaries
2175                 ShortGuid currentNetworkIdentifier = connection.ConnectionInfo.NetworkIdentifier;
2176
2177                 //We establish whether we have already done this step
2178                 if ((allConnectionsById.ContainsKey(currentNetworkIdentifier) &&
2179                     allConnectionsById[currentNetworkIdentifier].ContainsKey(connection.ConnectionInfo.ConnectionType) &&
2180                     allConnectionsById[currentNetworkIdentifier][connection.ConnectionInfo.ConnectionType].Contains(connection))
2181                     ||
2182                     (allConnectionsByEndPoint.ContainsKey(connection.ConnectionInfo.RemoteEndPoint) &&
2183                     allConnectionsByEndPoint[connection.ConnectionInfo.RemoteEndPoint].ContainsKey(connection.ConnectionInfo.ConnectionType)))
2184                 {
2185                     //Maintain a reference if this is our first connection close
2186                     returnValue = true;
2187                 }
2188
2189                 //Keep a reference of the connection for possible debugging later
2190                 if (maintainConnectionInfoHistory)
2191                 {
2192                     if (oldNetworkIdentifierToConnectionInfo.ContainsKey(currentNetworkIdentifier))
2193                     {
2194                         if (oldNetworkIdentifierToConnectionInfo[currentNetworkIdentifier].ContainsKey(connection.ConnectionInfo.ConnectionType))
2195                             oldNetworkIdentifierToConnectionInfo[currentNetworkIdentifier][connection.ConnectionInfo.ConnectionType].Add(connection.ConnectionInfo);
2196                         else
2197                             oldNetworkIdentifierToConnectionInfo[currentNetworkIdentifier].Add(connection.ConnectionInfo.ConnectionType, new List<ConnectionInfo>() { connection.ConnectionInfo });
2198                     }
2199                     else
2200                         oldNetworkIdentifierToConnectionInfo.Add(currentNetworkIdentifier, new Dictionary<ConnectionType, List<ConnectionInfo>>() { { connection.ConnectionInfo.ConnectionType, new List<ConnectionInfo>() { connection.ConnectionInfo } } });
2201                 }
2202
2203                 if (allConnectionsById.ContainsKey(currentNetworkIdentifier) &&
2204                         allConnectionsById[currentNetworkIdentifier].ContainsKey(connection.ConnectionInfo.ConnectionType))
2205                 {
2206                     //if (!allConnectionsById[currentNetworkIdentifier][connection.ConnectionInfo.ConnectionType].Contains(connection))
2207                     //    throw new ConnectionShutdownException("A reference to the connection being closed was not found in the allConnectionsById dictionary.");
2208                     //else
2209                     if (allConnectionsById[currentNetworkIdentifier][connection.ConnectionInfo.ConnectionType].Contains(connection))
2210                         allConnectionsById[currentNetworkIdentifier][connection.ConnectionInfo.ConnectionType].Remove(connection);
2211
2212                     //Remove the connection type reference if it is empty
2213                     if (allConnectionsById[currentNetworkIdentifier][connection.ConnectionInfo.ConnectionType].Count == 0)
2214                         allConnectionsById[currentNetworkIdentifier].Remove(connection.ConnectionInfo.ConnectionType);
2215
2216                     //Remove the identifier reference
2217                     if (allConnectionsById[currentNetworkIdentifier].Count == 0)
2218                         allConnectionsById.Remove(currentNetworkIdentifier);
2219
2220                     if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Removed connection reference by ID for " + connection.ConnectionInfo);
2221                 }
2222
2223                 //We can now remove this connection by end point as well
2224                 if (allConnectionsByEndPoint.ContainsKey(connection.ConnectionInfo.RemoteEndPoint))
2225                 {
2226                     if (allConnectionsByEndPoint[connection.ConnectionInfo.RemoteEndPoint].ContainsKey(connection.ConnectionInfo.ConnectionType))
2227                         allConnectionsByEndPoint[connection.ConnectionInfo.RemoteEndPoint].Remove(connection.ConnectionInfo.ConnectionType);
2228
2229                     //If this was the last connection type for this endpoint we can remove the endpoint reference as well
2230                     if (allConnectionsByEndPoint[connection.ConnectionInfo.RemoteEndPoint].Count == 0)
2231                         allConnectionsByEndPoint.Remove(connection.ConnectionInfo.RemoteEndPoint);
2232
2233                     if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Removed connection reference by endPoint for " + connection.ConnectionInfo);
2234                 }
2235                 #endregion
2236             }
2237
2238             return returnValue;
2239         }
2240
2241         /// <summary>
2242         /// Adds a reference by IPEndPoint to the provided connection within networkComms.
2243         /// </summary>
2244         /// <param name="connection"></param>
2245         /// <param name="endPointToUse">An optional override which forces a specific IPEndPoint</param>
2246         internal static void AddConnectionByReferenceEndPoint(Connection connection, IPEndPoint endPointToUse = null)
2247         {
2248             if (NetworkComms.LoggingEnabled)
2249                 NetworkComms.Logger.Trace("Adding connection reference by endPoint. Connection=‘"+connection.ConnectionInfo+"‘." +
2250                     (endPointToUse != null ? " Provided override endPoint of " + endPointToUse.Address + ":" + endPointToUse.Port.ToString() : ""));
2251
2252             //If the remoteEndPoint is IPAddress.Any we don‘t record it by endPoint
2253             if (connection.ConnectionInfo.RemoteEndPoint.Address.Equals(IPAddress.Any) || (endPointToUse != null && endPointToUse.Address.Equals(IPAddress.Any)))
2254                 return;
2255
2256             if (connection.ConnectionInfo.ConnectionState == ConnectionState.Established || connection.ConnectionInfo.ConnectionState == ConnectionState.Shutdown)
2257                 throw new ConnectionSetupException("Connection reference by endPoint should only be added before a connection is established. This is to prevent duplicate connections.");
2258
2259             if (endPointToUse == null) endPointToUse = connection.ConnectionInfo.RemoteEndPoint;
2260
2261             //We can double check for an existing connection here first so that it occurs outside the lock
2262             Connection existingConnection = GetExistingConnection(endPointToUse, connection.ConnectionInfo.ConnectionType);
2263             if (existingConnection != null && existingConnection.ConnectionInfo.ConnectionState == ConnectionState.Established && connection!=existingConnection)
2264                 existingConnection.ConnectionAlive();
2265
2266             //How do we prevent multiple threads from trying to create a duplicate connection??
2267             lock (globalDictAndDelegateLocker)
2268             {
2269                 //We now check for an existing connection again from within the lock
2270                 if (ConnectionExists(endPointToUse, connection.ConnectionInfo.ConnectionType))
2271                 {
2272                     //If a connection still exist we don‘t assume it is the same as above
2273                     existingConnection = GetExistingConnection(endPointToUse, connection.ConnectionInfo.ConnectionType);
2274                     if (existingConnection != connection)
2275                     {
2276                         throw new DuplicateConnectionException("A different connection already exists with the desired endPoint (" + endPointToUse.Address + ":" + endPointToUse.Port.ToString() + "). This can occasionaly occur if two peers try to connect to each other simultaneously. New connection is " + (existingConnection.ConnectionInfo.ServerSide ? "server side" : "client side") + " - " + connection.ConnectionInfo +
2277                             ". Existing connection is " + (existingConnection.ConnectionInfo.ServerSide ? "server side" : "client side") + ", " + existingConnection.ConnectionInfo.ConnectionState.ToString() + " - " + (existingConnection.ConnectionInfo.ConnectionState == ConnectionState.Establishing ? "creationTime:" + existingConnection.ConnectionInfo.ConnectionCreationTime.ToString() : "establishedTime:" + existingConnection.ConnectionInfo.ConnectionEstablishedTime.ToString()) + " - " + " details - " + existingConnection.ConnectionInfo);
2278                     }
2279                     else
2280                     {
2281                         //We have just tried to add the same reference twice, no need to do anything this time around
2282                     }
2283                 }
2284                 else
2285                 {
2286 #if FREETRIAL
2287                     //If this is a free trial we only allow a single connection. We will throw an exception if any connections already exist
2288                     if (TotalNumConnections() != 0)
2289                         throw new NotSupportedException("Unable to create connection as this version of NetworkComms.Net is limited to only one connection. Please purchase a commerical license from www.networkcomms.net which supports an unlimited number of connections.");
2290 #endif
2291
2292                     //Add reference to the endPoint dictionary
2293                     if (allConnectionsByEndPoint.ContainsKey(endPointToUse))
2294                     {
2295                         if (allConnectionsByEndPoint[endPointToUse].ContainsKey(connection.ConnectionInfo.ConnectionType))
2296                             throw new Exception("Idiot check fail. The method ConnectionExists should have prevented execution getting here!!");
2297                         else
2298                             allConnectionsByEndPoint[endPointToUse].Add(connection.ConnectionInfo.ConnectionType, connection);
2299                     }
2300                     else
2301                         allConnectionsByEndPoint.Add(endPointToUse, new Dictionary<ConnectionType, Connection>() { { connection.ConnectionInfo.ConnectionType, connection } });
2302                 }
2303             }
2304         }
2305
2306         /// <summary>
2307         /// Update the endPoint reference for the provided connection with the newEndPoint. If there is no change just returns
2308         /// </summary>
2309         /// <param name="connection"></param>
2310         /// <param name="newRemoteEndPoint"></param>
2311         internal static void UpdateConnectionReferenceByEndPoint(Connection connection, IPEndPoint newRemoteEndPoint)
2312         {
2313             if (NetworkComms.LoggingEnabled)
2314                 NetworkComms.Logger.Trace("Updating connection reference by endPoint. Connection=‘" + connection.ConnectionInfo + "‘." + (newRemoteEndPoint != null ? " Provided new endPoint of " + newRemoteEndPoint.Address + ":" + newRemoteEndPoint.Port.ToString() : ""));
2315
2316             if (!connection.ConnectionInfo.RemoteEndPoint.Equals(newRemoteEndPoint))
2317             {
2318                 lock (globalDictAndDelegateLocker)
2319                 {
2320                     RemoveConnectionReference(connection, false);
2321                     AddConnectionByReferenceEndPoint(connection, newRemoteEndPoint);
2322                 }
2323             }
2324         }
2325
2326         /// <summary>
2327         /// Add a reference by networkIdentifier to the provided connection within NetworkComms. Requires a reference by IPEndPoint to already exist.
2328         /// </summary>
2329         /// <param name="connection"></param>
2330         internal static void AddConnectionReferenceByIdentifier(Connection connection)
2331         {
2332             if (!(connection.ConnectionInfo.ConnectionState == ConnectionState.Established) || connection.ConnectionInfo.ConnectionState == ConnectionState.Shutdown)
2333                 throw new ConnectionSetupException("Connection reference by identifier should only be added once a connection is established. This is to prevent duplicate connections.");
2334
2335             if (connection.ConnectionInfo.NetworkIdentifier == ShortGuid.Empty)
2336                 throw new ConnectionSetupException("Should not be calling AddConnectionByIdentifierReference unless the connection remote identifier has been set.");
2337
2338             if (NetworkComms.LoggingEnabled)
2339                 NetworkComms.Logger.Trace("Adding connection reference by identifier. Connection=" + connection.ConnectionInfo + ".");
2340
2341             lock (globalDictAndDelegateLocker)
2342             {
2343                 //There should already be a reference to this connection in the endPoint dictionary
2344                 if (!ConnectionExists(connection.ConnectionInfo.RemoteEndPoint, connection.ConnectionInfo.ConnectionType))
2345                     throw new ConnectionSetupException("A reference by identifier should only be added if a reference by endPoint already exists.");
2346
2347                 //Check for an existing reference first, if there is one and it matches this connection then no worries
2348                 if (allConnectionsById.ContainsKey(connection.ConnectionInfo.NetworkIdentifier))
2349                 {
2350                     if (allConnectionsById[connection.ConnectionInfo.NetworkIdentifier].ContainsKey(connection.ConnectionInfo.ConnectionType))
2351                     {
2352                         if (!allConnectionsById[connection.ConnectionInfo.NetworkIdentifier][connection.ConnectionInfo.ConnectionType].Contains(connection))
2353                         {
2354                             foreach (var current in allConnectionsById[connection.ConnectionInfo.NetworkIdentifier][connection.ConnectionInfo.ConnectionType])
2355                             {
2356                                 if (current.ConnectionInfo.RemoteEndPoint.Equals(connection.ConnectionInfo.RemoteEndPoint))
2357                                     throw new ConnectionSetupException("A different connection to the same remoteEndPoint already exists. Duplicate connections should be prevented elsewhere. Existing connection " + current.ConnectionInfo + ", new connection " + connection.ConnectionInfo);
2358                             }
2359                         }
2360                         else
2361                         {
2362                             //We are trying to add the same connection twice, so just do nothing here.
2363                         }
2364                     }
2365                     else
2366                         allConnectionsById[connection.ConnectionInfo.NetworkIdentifier].Add(connection.ConnectionInfo.ConnectionType, new List<Connection>() { connection });
2367                 }
2368                 else
2369                     allConnectionsById.Add(connection.ConnectionInfo.NetworkIdentifier, new Dictionary<ConnectionType, List<Connection>>() { { connection.ConnectionInfo.ConnectionType, new List<Connection>() {connection}} });
2370             }
2371         }
2372         #endregion
2373     }

基于此系统编写了几个都不大的小项目

服务器端采用  win server 2003   .net框架2.0  数据库mssql2005

客户端 红米手机   或者其他安卓手机

能够顺利的实现在手机上提交数据到服务器,或者从服务器上获取数据。

这几天有时间会整理一个小demo出来,给大家参考,敬请期待。

www.cnblogs.com/networkcomms

www.networkcomms.cn(建设中)

时间: 2024-10-26 21:17:40

用c#开发安卓程序 (xamarin.android)的相关文章

用c#开发安卓程序 (xamarin.android)系列之二 简单的聊天程序

networkcomm.net 网络通信框架来自于英国剑桥,其开源版本2.3.1 中自带了一个编写android的例子,可以很好的帮助我们入门. 此示例的功能,是在2个安卓手机上,输入对方的IP和端口,能够实现聊天功能. 把代码放上,供大家一览 using System; using Android.App; using Android.Content; using Android.Runtime; using Android.Views; using Android.Widget; using

Delphi开发安卓程序的感受

Delphi XE7开发安卓程序,界面开发速度非常快,这是eclipse无法比的,还有就是界面自适应能力很棒,我在不同版本和尺寸的设备中测试,运行良好,这些设备包括:三星I9100(安卓2.3:不到5寸屏):三星T311(安卓4:8寸屏):三星T705C(安卓4:8.4寸屏):华为X1(安卓4:7寸屏). Delphi开发的安卓程序,可以用很多Delphi的资源,我尝试用Indy TCP连接TCP服务器,已经成功,可以做EACH操作,类似资源应该有很多可以使用,这对Delphi程序员来说是非常棒

isual Studio跨平台开发实战(4) - Xamarin Android基本控制项介绍

原文 isual Studio跨平台开发实战(4) - Xamarin Android基本控制项介绍 前言 不同于iOS, Xamarin 在Visual Studio中针对Android, 可以直接设计使用者介面. 在本篇教学文章中, 笔者会针对Android的专案目录结构以及基本控制项进行介绍, 包含TextView, EditView, Toggle/ Switch以及Seekbar控制项. Android专案目录结构 在Visual Studio建立Android 应用程式专案后, ??

Visual Studio跨平台开发实战(5) - Xamarin Android多页面应用程式开发

原文 Visual Studio跨平台开发实战(5) - Xamarin Android多页面应用程式开发 前言 大部份的Andr??oid 都具有实体或虚拟的Back键. 因此在处理多页面应用程式时, 与先前所介绍的iOS Navigation controller 比较起来会简单许多. 1. 开启Visual Studio 并新增Android Application 专案并命名为Lab4-MultiScreen   2. 在Layout资料夹中新增Second.axml   在Second

[Xamarin.Android] 发布NuGet套件

[Xamarin.Android] 发布NuGet套件 前言 在Xamarin中,可以将自己开发的项目包装成为NuGet套件发布至NuGet Server,来提供其他开发人员使用.本篇介绍如何封装并发布NuGet套件,让开发人员能自行建立NuGet Server中的NuGet套件,为自己留个纪录也希望能帮助到有需要的开发人员. 封装 首先将要封装成NuGet套件的程序代码,建立为Class Library (Android)类型的项目.在项目完成编译之后取得产出的dll档,这个dll档就是要封装

如何用 纯C++(ndk)开发安卓应用 ?

如何安装安卓的开发环境以及怎么设置ndk的环境变量等在前边的文章已经有了详细的讲解,在这里我就不再说明,如果有不会安装和设置环境的,请先参考安卓环境搭建的内容. 好,假设以及安装好了ndk,使用纯c++开发安卓程序,下边是详细的步骤与说明: 1.编写入口函数 android_main为入口函数,和C++中的main函数是一样的.这里创建CELLAndroidApp的对象,直接调用main函数. void android_main(struct android_app* state) { CELL

C++开发安卓、windows下搭建Android NDK开发环境

1. NDK(Native Development Kit) 1.1 NDK简介 Android NDK是一套允许开发人员使用本地代码(如C/C++)进行Android APP功能开发的工具,通过这个工具,我们可以把用C/C++代码编译成可以直接运行在Android平台上的本地代码,这些本地代码以动态链接库( *.so )的形式存在,也正因为这样,我们可以通过复用这些动态链接库从而复用本地代码. 那么,通过NDK这个开发工具包,那么我们是否可以将一个APK完全使用C/C++来编写呢? 答案是不可

Xamarin.Android开发实践(十五)

Xamarin.Android学习之应用程序首选项 一.前言 任何App都会存在设置界面,如果开发者利用普通控件并绑定监听事件保存设置,这 一过程会非常的枯燥,而且耗时.我们可以看到Android系统的设置界面里面的选项如此之多,是不是都是这样开发的呢?其实不然,Android已经给 我们提供了专门设计这一功能的技术,叫应用程序首选项,今天我们将学习如何使用他们来开发配置界面以及功能. 二.准备工作 首先需要理解的就是我们设置界面还是需要控件的,但是我们所使用的控件不在是普通的控件,下面我们来简

xamarin.Android 开发环境

纯粹的.net程序员,没有任何android开发经验.连续折腾三天,才配置完成的xamarin.android开发环境,完成Hello World ,一路都是坑啊. 一.得新安装VS2015,把移动开发选项勾上:xamarin ,android有SDK我也勾了,但是没有用,装得很慢,也装不上.最终的结果是,我把所有与android相关的都删了,手动安装和配置JDK.Android SDK.Android NDK. 推荐个好地方:http://www.androiddevtools.cn/ 我所有