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

NetworkComms网络通信框架序言

 作为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     }
View Code

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

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

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

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

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

www.cnblogs.com/networkcomms

www.networkcomms.cn(建设中)

你可能感兴趣的:(android)