首页 > 代码库 > 用c#开发安卓程序 (xamarin.android)

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

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

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

技术分享
   1  public static class NetworkComms   2     {   3         /// <summary>   4         /// Static constructor which sets comm default values   5         /// </summary>   6         static NetworkComms()   7         {   8             //Generally comms defaults are defined here   9             NetworkIdentifier = ShortGuid.NewGuid();  10             NetworkLoadUpdateWindowMS = 2000;  11   12             InterfaceLinkSpeed = 95000000;  13   14             DefaultListenPort = 10000;  15             ListenOnAllAllowedInterfaces = true;  16   17             CheckSumMismatchSentPacketCacheMaxByteLimit = 75000;  18             MinimumSentPacketCacheTimeMinutes = 1;  19   20             ConnectionEstablishTimeoutMS = 10000;  21             PacketConfirmationTimeoutMS = 5000;  22             ConnectionAliveTestTimeoutMS = 1000;  23   24 #if SILVERLIGHT || WINDOWS_PHONE  25             CurrentRuntimeEnvironment = RuntimeEnvironment.WindowsPhone_Silverlight;  26             SendBufferSizeBytes = ReceiveBufferSizeBytes = 8000;  27 #elif iOS  28             CurrentRuntimeEnvironment = RuntimeEnvironment.Xamarin_iOS;  29             SendBufferSizeBytes = ReceiveBufferSizeBytes = 8000;  30 #elif ANDROID  31             CurrentRuntimeEnvironment = RuntimeEnvironment.Xamarin_Android;  32             SendBufferSizeBytes = ReceiveBufferSizeBytes = 8000;  33 #elif NET2  34             if (Type.GetType("Mono.Runtime") != null)  35             {  36                 CurrentRuntimeEnvironment = RuntimeEnvironment.Mono_Net2;  37                 //Mono send buffer smaller as different large object heap limit  38                 SendBufferSizeBytes = ReceiveBufferSizeBytes = 8000;  39             }  40             else  41             {  42                 CurrentRuntimeEnvironment = RuntimeEnvironment.Native_Net2;  43                 SendBufferSizeBytes = ReceiveBufferSizeBytes = 80000;  44             }  45 #elif NET35  46             if (Type.GetType("Mono.Runtime") != null)  47             {  48                 CurrentRuntimeEnvironment = RuntimeEnvironment.Mono_Net35;  49                 //Mono send buffer smaller as different large object heap limit  50                 SendBufferSizeBytes = ReceiveBufferSizeBytes = 8000;  51             }  52             else  53             {  54                 CurrentRuntimeEnvironment = RuntimeEnvironment.Native_Net35;  55                 SendBufferSizeBytes = ReceiveBufferSizeBytes = 80000;  56             }  57 #else  58             if (Type.GetType("Mono.Runtime") != null)  59             {  60                 CurrentRuntimeEnvironment = RuntimeEnvironment.Mono_Net4;  61                 //Mono send buffer smaller as different large object heap limit  62                 SendBufferSizeBytes = ReceiveBufferSizeBytes = 8000;  63             }  64             else  65             {  66                 CurrentRuntimeEnvironment = RuntimeEnvironment.Native_Net4;  67                 SendBufferSizeBytes = ReceiveBufferSizeBytes = 80000;  68             }  69 #endif  70   71             //We want to instantiate our own thread pool here  72             CommsThreadPool = new CommsThreadPool(1, Environment.ProcessorCount*2, Environment.ProcessorCount * 20, new TimeSpan(0, 0, 10));  73   74             //Initialise the core extensions  75             DPSManager.AddDataSerializer<ProtobufSerializer>();  76   77             DPSManager.AddDataSerializer<NullSerializer>();  78             DPSManager.AddDataProcessor<SevenZipLZMACompressor.LZMACompressor>();  79   80 #if !FREETRIAL  81             //Only the full version includes the encrypter  82             DPSManager.AddDataProcessor<RijndaelPSKEncrypter>();  83 #endif  84   85 #if !WINDOWS_PHONE  86             DPSManager.AddDataSerializer<BinaryFormaterSerializer>();  87 #endif  88   89             InternalFixedSendReceiveOptions = new SendReceiveOptions(DPSManager.GetDataSerializer<ProtobufSerializer>(),  90                 new List<DataProcessor>(),  91                 new Dictionary<string, string>());  92   93             DefaultSendReceiveOptions = new SendReceiveOptions(DPSManager.GetDataSerializer<ProtobufSerializer>(),  94                 new List<DataProcessor>() { DPSManager.GetDataProcessor<SevenZipLZMACompressor.LZMACompressor>() },  95                 new Dictionary<string, string>());  96         }  97   98         #region Local Host Information  99         /// <summary> 100         /// Returns the current machine hostname 101         /// </summary> 102         public static string HostName 103         { 104             get  105             { 106 #if WINDOWS_PHONE 107                 return Windows.Networking.Connectivity.NetworkInformation.GetInternetConnectionProfile().ToString(); 108 #else 109                 return Dns.GetHostName();  110 #endif 111             } 112         } 113  114         /// <summary> 115         /// If set NetworkCommsDotNet will only operate on matching IP Addresses. Also see <see cref="AllowedAdaptorNames"/>. 116         /// Correct format is string[] { "192.168", "213.111.10" }. If multiple prefixes are provided the earlier prefix, if found, takes priority. 117         /// </summary> 118         public static string[] AllowedIPPrefixes { get; set; } 119  120         /// <summary> 121         ///  If set NetworkCommsDotNet will only operate on specified adaptors. Correct format is string[] { "eth0", "en0", "wlan0" }. 122         /// </summary> 123         public static string[] AllowedAdaptorNames { get; set; } 124  125         /// <summary> 126         /// Returns all allowed local IP addresses.  127         /// If <see cref="AllowedAdaptorNames"/> has been set only returns IP addresses corresponding with specified adaptors. 128         /// If <see cref="AllowedIPPrefixes"/> has been set only returns matching addresses ordered in descending preference. i.e. Most preffered at [0]. 129         /// </summary> 130         /// <returns></returns> 131         public static List<IPAddress> AllAllowedIPs() 132         { 133  134 #if WINDOWS_PHONE 135             //On windows phone we simply ignore ip addresses from the autoassigned range as well as those without a valid prefix 136             List<IPAddress> allowedIPs = new List<IPAddress>(); 137  138             foreach (var hName in Windows.Networking.Connectivity.NetworkInformation.GetHostNames()) 139             { 140                 if (!hName.DisplayName.StartsWith("169.254")) 141                 { 142                     if (AllowedIPPrefixes != null) 143                     { 144                         bool valid = false; 145  146                         for (int i = 0; i < AllowedIPPrefixes.Length; i++) 147                             valid |= hName.DisplayName.StartsWith(AllowedIPPrefixes[i]); 148                                  149                         if(valid) 150                             allowedIPs.Add(IPAddress.Parse(hName.DisplayName)); 151                     } 152                     else 153                         allowedIPs.Add(IPAddress.Parse(hName.DisplayName)); 154                 } 155             } 156  157             return allowedIPs; 158 #else 159  160             //We want to ignore IP‘s that have been autoassigned 161             //169.254.0.0 162             IPAddress autoAssignSubnetv4 = new IPAddress(new byte[] { 169, 254, 0, 0 }); 163             //255.255.0.0 164             IPAddress autoAssignSubnetMaskv4 = new IPAddress(new byte[] { 255, 255, 0, 0 }); 165  166             List<IPAddress> validIPAddresses = new List<IPAddress>(); 167             IPComparer comparer = new IPComparer(); 168  169 #if ANDROID 170  171             var iFaces = Java.Net.NetworkInterface.NetworkInterfaces; 172             while (iFaces.HasMoreElements) 173             { 174                 bool interfaceValid = false; 175                 var iFace = iFaces.NextElement() as Java.Net.NetworkInterface; 176                 var javaAddresses = iFace.InetAddresses; 177  178                 while (javaAddresses.HasMoreElements) 179                 { 180                     var javaAddress = javaAddresses.NextElement() as Java.Net.InetAddress; 181                     IPAddress address = default(IPAddress); 182                     if (IPAddress.TryParse(javaAddress.HostAddress, out address)) 183                     { 184                         if (address.AddressFamily == AddressFamily.InterNetwork || address.AddressFamily == AddressFamily.InterNetworkV6) 185                         { 186                             if (AllowedAdaptorNames != null) 187                             { 188                                 foreach (var id in AllowedAdaptorNames) 189                                     if (id == iFace.Name) 190                                     { 191                                         interfaceValid = true; 192                                         break; 193                                     } 194                             } 195                             else 196                                 interfaceValid = true; 197  198                             if (interfaceValid) 199                                 break; 200                         } 201                     } 202                 } 203  204                 if (!interfaceValid) 205                     continue; 206  207                 javaAddresses = iFace.InetAddresses; 208  209                 while (javaAddresses.HasMoreElements) 210                 { 211                     var javaAddress = javaAddresses.NextElement() as Java.Net.InetAddress; 212                     IPAddress address = default(IPAddress); 213  214                     if (IPAddress.TryParse(javaAddress.HostAddress, out address)) 215                     { 216                         if (address.AddressFamily == AddressFamily.InterNetwork || address.AddressFamily == AddressFamily.InterNetworkV6) 217                         { 218                             if (!IsAddressInSubnet(address, autoAssignSubnetv4, autoAssignSubnetMaskv4)) 219                             { 220                                 bool allowed = false; 221  222                                 if (AllowedAdaptorNames != null) 223                                 { 224                                     foreach (var id in AllowedAdaptorNames) 225                                     { 226                                         if (id == iFace.Name) 227                                         { 228                                             allowed = true; 229                                             break; 230                                         } 231                                     } 232                                 } 233                                 else 234                                     allowed = true; 235  236                                 if (!allowed) 237                                     continue; 238  239                                 allowed = false; 240  241                                 if (AllowedIPPrefixes != null) 242                                 { 243                                     foreach (var ip in AllowedIPPrefixes) 244                                     { 245                                         if (comparer.Equals(address.ToString(), ip)) 246                                         { 247                                             allowed = true; 248                                             break; 249                                         } 250                                     } 251                                 } 252                                 else 253                                     allowed = true; 254  255                                 if (!allowed) 256                                     continue; 257  258                                 if (address != IPAddress.None) 259                                     validIPAddresses.Add(address); 260                             } 261                         } 262                     } 263                 }     264             } 265  266 #else 267  268  269             foreach (var iFace in NetworkInterface.GetAllNetworkInterfaces()) 270             { 271                 bool interfaceValid = false; 272                 var unicastAddresses = iFace.GetIPProperties().UnicastAddresses; 273  274                 foreach (var address in unicastAddresses) 275                 { 276                     if (address.Address.AddressFamily == AddressFamily.InterNetwork || address.Address.AddressFamily == AddressFamily.InterNetworkV6) 277                     { 278                         if (AllowedAdaptorNames != null) 279                         { 280                             foreach (var id in AllowedAdaptorNames) 281                                 if (iFace.Id == id) 282                                 { 283                                     interfaceValid = true; 284                                     break; 285                                 } 286                         } 287                         else 288                             interfaceValid = true; 289  290                         if (interfaceValid) 291                             break; 292                     } 293                 } 294  295                 if (!interfaceValid) 296                     continue; 297  298                 foreach (var address in unicastAddresses) 299                 { 300                     var addressInformation = address.Address; 301                     if (addressInformation.AddressFamily == AddressFamily.InterNetwork || addressInformation.AddressFamily == AddressFamily.InterNetworkV6) 302                     { 303                         if (!IsAddressInSubnet(addressInformation, autoAssignSubnetv4, autoAssignSubnetMaskv4)) 304                         { 305                             bool allowed = false; 306  307                             if (AllowedAdaptorNames != null) 308                             { 309                                 foreach (var id in AllowedAdaptorNames) 310                                 { 311                                     if(id == iFace.Id) 312                                     { 313                                         allowed = true; 314                                         break; 315                                     } 316                                 } 317                             } 318                             else 319                                 allowed = true; 320  321                             if (!allowed) 322                                 continue; 323  324                             allowed = false; 325  326                             if (AllowedIPPrefixes != null) 327                             { 328                                 foreach (var ip in AllowedIPPrefixes) 329                                 { 330                                     if (comparer.Equals(addressInformation.ToString(), ip)) 331                                     { 332                                         allowed = true; 333                                         break; 334                                     } 335                                 } 336                             } 337                             else 338                                 allowed = true; 339  340                             if (!allowed) 341                                 continue; 342  343                             if (addressInformation != IPAddress.None) 344                                 validIPAddresses.Add(addressInformation); 345                         } 346                     } 347                 }                348             } 349 #endif 350  351             if (AllowedIPPrefixes != null) 352             { 353                 validIPAddresses.Sort((a, b) => 354                 { 355                     for (int i = 0; i < AllowedIPPrefixes.Length; i++) 356                     { 357                         if (a.ToString().StartsWith(AllowedIPPrefixes[i])) 358                         { 359                             if (b.ToString().StartsWith(AllowedIPPrefixes[i])) 360                                 return 0; 361                             else 362                                 return -1; 363                         } 364                         else if (b.ToString().StartsWith(AllowedIPPrefixes[i])) 365                             return 1; 366                     } 367  368                     return 0; 369                 }); 370             } 371  372             return validIPAddresses; 373 #endif 374         } 375  376         /// <summary> 377         /// Custom comparer for IP addresses. Used by <see cref="AllAllowedIPs"/> 378         /// </summary> 379         class IPComparer : IEqualityComparer<string> 380         { 381             // Products are equal if their names and product numbers are equal. 382             public bool Equals(string x, string y) 383             { 384                 //Check whether the compared objects reference the same data. 385                 if (Object.ReferenceEquals(x, y)) return true; 386  387                 //Check whether any of the compared objects is null. 388                 if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null)) 389                     return false; 390  391                 return (y.StartsWith(x) || x.StartsWith(y)); 392             } 393  394             // If Equals() returns true for a pair of objects  395             // then GetHashCode() must return the same value for these objects. 396             public int GetHashCode(string ipAddress) 397             { 398                 return ipAddress.GetHashCode(); 399             } 400         } 401  402         /// <summary> 403         /// Returns true if the provided address exists within the provided subnet. 404         /// </summary> 405         /// <param name="address">The address to check, i.e. 192.168.0.10</param> 406         /// <param name="subnet">The subnet, i.e. 192.168.0.0</param> 407         /// <param name="mask">The subnet mask, i.e. 255.255.255.0</param> 408         /// <returns>True if address is in the provided subnet</returns> 409         public static bool IsAddressInSubnet(IPAddress address, IPAddress subnet, IPAddress mask) 410         { 411             if (address == null) throw new ArgumentNullException("address", "Provided IPAddress cannot be null."); 412             if (subnet == null) throw new ArgumentNullException("subnet", "Provided IPAddress cannot be null."); 413             if (mask == null) throw new ArgumentNullException("mask", "Provided IPAddress cannot be null."); 414  415             //Catch for IPv6 416             if (subnet.AddressFamily == AddressFamily.InterNetworkV6 ||  417                 mask.AddressFamily == AddressFamily.InterNetworkV6) 418                 throw new NotImplementedException("This method does not yet support IPv6. Please contact NetworkComms.Net support if you would like this functionality."); 419             //If we have provided IPV4 subnets and masks and we have an ipv6 address then return false 420             else if (address.AddressFamily == AddressFamily.InterNetworkV6) 421                 return false; 422  423             byte[] addrBytes = address.GetAddressBytes(); 424             byte[] maskBytes = mask.GetAddressBytes(); 425             byte[] maskedAddressBytes = new byte[addrBytes.Length]; 426  427             //Catch for IPv6 428             if (maskBytes.Length < maskedAddressBytes.Length) 429                 return false; 430  431             for (int i = 0; i < maskedAddressBytes.Length; ++i) 432                 maskedAddressBytes[i] = (byte)(addrBytes[i] & maskBytes[i]); 433  434             IPAddress maskedAddress = new IPAddress(maskedAddressBytes); 435             bool equal = subnet.Equals(maskedAddress); 436  437             return equal; 438         } 439  440         /// <summary> 441         /// The default port NetworkCommsDotNet will operate on 442         /// </summary> 443         public static int DefaultListenPort { get; set; } 444  445         /// <summary> 446         /// The local identifier for this instance of NetworkCommsDotNet. This is an application unique identifier. 447         /// </summary> 448         public static ShortGuid NetworkIdentifier { get; private set; } 449  450         /// <summary> 451         /// The current runtime environment. Detected automatically on startup. Performance may be adversly affected if this is changed. 452         /// </summary> 453         public static RuntimeEnvironment CurrentRuntimeEnvironment { get; set; } 454  455         /// <summary> 456         /// An internal random object 457         /// </summary> 458         internal static Random randomGen = new Random(); 459  460         /// <summary> 461         /// A single boolean used to control a NetworkCommsDotNet shutdown 462         /// </summary> 463         internal static volatile bool commsShutdown; 464  465         /// <summary> 466         /// A running total of the number of packets sent on all connections. Used to initialise packet sequence counters to ensure duplicates can not occur. 467         /// </summary> 468         internal static long totalPacketSendCount; 469  470         /// <summary> 471         /// The number of millisconds over which to take an instance load (CurrentNetworkLoad) to be used in averaged values (AverageNetworkLoad).  472         /// Default is 2000ms. Shorter values can be used but less than 200ms may cause significant errors in the value of returned value, especially in mono environments. 473         /// </summary> 474         public static int NetworkLoadUpdateWindowMS { get; set; } 475  476         private static double currentNetworkLoadIncoming; 477         private static double currentNetworkLoadOutgoing; 478 #if !WINDOWS_PHONE && !ANDROID 479         private static Thread NetworkLoadThread = null; 480         private static CommsMath currentNetworkLoadValuesIncoming; 481         private static CommsMath currentNetworkLoadValuesOutgoing; 482         private static ManualResetEvent NetworkLoadThreadWait; 483 #endif 484  485         /// <summary> 486         /// The interface link speed in bits/sec used for network load calculations. Default is 100Mb/sec 487         /// </summary> 488         public static long InterfaceLinkSpeed { get; set; } 489  490         /// <summary> 491         /// Returns the current instance network usage, as a value between 0 and 1. Returns the largest value for any available network adaptor. Triggers load analysis upon first call. 492         /// </summary> 493         public static double CurrentNetworkLoadIncoming 494         { 495             get 496             { 497 #if !WINDOWS_PHONE && !ANDROID 498                 //We start the load thread when we first access the network load 499                 //this helps cut down on uncessary threads if unrequired 500                 if (!commsShutdown && NetworkLoadThread == null) 501                 { 502                     lock (globalDictAndDelegateLocker) 503                     { 504                         if (!commsShutdown && NetworkLoadThread == null) 505                         { 506                             currentNetworkLoadValuesIncoming = new CommsMath(); 507                             currentNetworkLoadValuesOutgoing = new CommsMath(); 508  509                             NetworkLoadThread = new Thread(NetworkLoadWorker); 510                             NetworkLoadThread.Name = "NetworkLoadThread"; 511                             NetworkLoadThread.Start(); 512                         } 513                     } 514                 } 515 #endif 516                 return currentNetworkLoadIncoming; 517             } 518             private set { currentNetworkLoadIncoming = value; } 519         } 520  521         /// <summary> 522         /// Returns the current instance network usage, as a value between 0 and 1. Returns the largest value for any available network adaptor. Triggers load analysis upon first call. 523         /// </summary> 524         public static double CurrentNetworkLoadOutgoing 525         { 526             get 527             { 528 #if !WINDOWS_PHONE && !ANDROID 529                 //We start the load thread when we first access the network load 530                 //this helps cut down on uncessary threads if unrequired 531                 if (!commsShutdown && NetworkLoadThread == null) 532                 { 533                     lock (globalDictAndDelegateLocker) 534                     { 535                         if (!commsShutdown && NetworkLoadThread == null) 536                         { 537                             currentNetworkLoadValuesIncoming = new CommsMath(); 538                             currentNetworkLoadValuesOutgoing = new CommsMath(); 539  540                             NetworkLoadThread = new Thread(NetworkLoadWorker); 541                             NetworkLoadThread.Name = "NetworkLoadThread"; 542                             NetworkLoadThread.Start(); 543                         } 544                     } 545                 } 546 #endif 547                 return currentNetworkLoadOutgoing; 548             } 549             private set { currentNetworkLoadOutgoing = value; } 550         } 551  552         /// <summary> 553         /// Returns the averaged value of CurrentNetworkLoadIncoming, as a value between 0 and 1, for a time window of upto 254 seconds. Triggers load analysis upon first call. 554         /// </summary> 555         /// <param name="secondsToAverage">Number of seconds over which historial data should be used to arrive at an average</param> 556         /// <returns>Average network load as a double between 0 and 1</returns> 557         public static double AverageNetworkLoadIncoming(byte secondsToAverage) 558         { 559 #if !WINDOWS_PHONE && !ANDROID 560  561             if (!commsShutdown && NetworkLoadThread == null) 562             { 563                 lock (globalDictAndDelegateLocker) 564                 { 565                     if (!commsShutdown && NetworkLoadThread == null) 566                     { 567                         currentNetworkLoadValuesIncoming = new CommsMath(); 568                         currentNetworkLoadValuesOutgoing = new CommsMath(); 569  570                         NetworkLoadThread = new Thread(NetworkLoadWorker); 571                         NetworkLoadThread.Name = "NetworkLoadThread"; 572                         NetworkLoadThread.Start(); 573                     } 574                 } 575             } 576  577             return currentNetworkLoadValuesIncoming.CalculateMean((int)((secondsToAverage * 1000.0) / NetworkLoadUpdateWindowMS)); 578 #else 579             return 0; 580 #endif 581         } 582  583         /// <summary> 584         /// Returns the averaged value of CurrentNetworkLoadIncoming, as a value between 0 and 1, for a time window of upto 254 seconds. Triggers load analysis upon first call. 585         /// </summary> 586         /// <param name="secondsToAverage">Number of seconds over which historial data should be used to arrive at an average</param> 587         /// <returns>Average network load as a double between 0 and 1</returns> 588         public static double AverageNetworkLoadOutgoing(byte secondsToAverage) 589         { 590 #if !WINDOWS_PHONE && !ANDROID 591             if (!commsShutdown && NetworkLoadThread == null) 592             { 593                 lock (globalDictAndDelegateLocker) 594                 { 595                     if (!commsShutdown && NetworkLoadThread == null) 596                     { 597                         currentNetworkLoadValuesIncoming = new CommsMath(); 598                         currentNetworkLoadValuesOutgoing = new CommsMath(); 599  600                         NetworkLoadThread = new Thread(NetworkLoadWorker); 601                         NetworkLoadThread.Name = "NetworkLoadThread"; 602                         NetworkLoadThread.Start(); 603                     } 604                 } 605             } 606  607             return currentNetworkLoadValuesOutgoing.CalculateMean((int)((secondsToAverage * 1000.0) / NetworkLoadUpdateWindowMS)); 608 #else 609             return 0; 610 #endif 611         } 612  613         /// <summary> 614         /// Determines the most appropriate local end point to contact the provided remote end point.  615         /// Testing shows this method takes on average 1.6ms to return. 616         /// </summary> 617         /// <param name="remoteIPEndPoint">The remote end point</param> 618         /// <returns>The selected local end point</returns> 619         public static IPEndPoint BestLocalEndPoint(IPEndPoint remoteIPEndPoint) 620         { 621             if (remoteIPEndPoint == null) throw new ArgumentNullException("remoteIPEndPoint", "Provided IPEndPoint cannot be null."); 622  623 #if WINDOWS_PHONE 624             var t = Windows.Networking.Sockets.DatagramSocket.GetEndpointPairsAsync(new Windows.Networking.HostName(remoteIPEndPoint.Address.ToString()), remoteIPEndPoint.Port.ToString()).AsTask(); 625             if (t.Wait(20) && t.Result.Count > 0) 626             { 627                 var enumerator = t.Result.GetEnumerator(); 628                 enumerator.MoveNext(); 629  630                 var endpointPair = enumerator.Current;                 631                 return new IPEndPoint(IPAddress.Parse(endpointPair.LocalHostName.DisplayName.ToString()), int.Parse(endpointPair.LocalServiceName)); 632             } 633             else 634                 throw new ConnectionSetupException("Unable to determine correct local end point."); 635 #else 636             //We use UDP as its connectionless hence faster 637             IPEndPoint result; 638             using (Socket testSocket = new Socket(remoteIPEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp)) 639             { 640                 testSocket.Connect(remoteIPEndPoint); 641                 result = (IPEndPoint)testSocket.LocalEndPoint; 642             } 643  644             return result; 645 #endif 646         } 647  648 #if !WINDOWS_PHONE && !ANDROID 649         /// <summary> 650         /// Takes a network load snapshot (CurrentNetworkLoad) every NetworkLoadUpdateWindowMS 651         /// </summary> 652         private static void NetworkLoadWorker() 653         { 654             NetworkLoadThreadWait = new ManualResetEvent(false); 655              656             //Get all interfaces 657             NetworkInterface[] interfacesToUse = NetworkInterface.GetAllNetworkInterfaces(); 658              659             long[] startSent, startReceived, endSent, endReceived; 660  661             while (!commsShutdown) 662             { 663                 try 664                 { 665                     //we need to look at the load across all adaptors, by default we will probably choose the adaptor with the highest usage 666                     DateTime startTime = DateTime.Now; 667  668                     IPv4InterfaceStatistics[] stats = new IPv4InterfaceStatistics[interfacesToUse.Length]; 669                     startSent = new long[interfacesToUse.Length]; 670                     startReceived = new long[interfacesToUse.Length]; 671  672                     for (int i = 0; i < interfacesToUse.Length; ++i) 673                     { 674                         stats[i] = interfacesToUse[i].GetIPv4Statistics(); 675                         startSent[i] = stats[i].BytesSent; 676                         startReceived[i] = stats[i].BytesReceived; 677                     } 678                      679                     if (commsShutdown) return; 680  681                     //Thread.Sleep(NetworkLoadUpdateWindowMS); 682                     NetworkLoadThreadWait.WaitOne(NetworkLoadUpdateWindowMS); 683  684                     if (commsShutdown) return; 685  686                     stats = new IPv4InterfaceStatistics[interfacesToUse.Length]; 687                     endSent = new long[interfacesToUse.Length]; 688                     endReceived = new long[interfacesToUse.Length]; 689  690                     for (int i = 0; i < interfacesToUse.Length; ++i) 691                     { 692                         stats[i] = interfacesToUse[i].GetIPv4Statistics(); 693                         endSent[i] = stats[i].BytesSent; 694                         endReceived[i] = stats[i].BytesReceived; 695                     } 696                      697                     DateTime endTime = DateTime.Now; 698  699                     List<double> outUsage = new List<double>(); 700                     List<double> inUsage = new List<double>(); 701                     for(int i=0; i<startSent.Length; i++) 702                     { 703                         outUsage.Add((double)(endSent[i] - startSent[i]) / ((double)(InterfaceLinkSpeed * (endTime - startTime).TotalMilliseconds) / 8000)); 704                         inUsage.Add((double)(endReceived[i] - startReceived[i]) / ((double)(InterfaceLinkSpeed * (endTime - startTime).TotalMilliseconds) / 8000)); 705                     } 706  707                     //double loadValue = http://www.mamicode.com/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 provided1027                     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                 else1031                     globalIncomingPacketUnwrappers.Add(packetTypeStr, new PacketTypeUnwrapper(packetTypeStr, sendReceiveOptions));1032                 1033 1034                 //Ad the handler to the list1035                 if (globalIncomingPacketHandlers.ContainsKey(packetTypeStr))1036                 {1037                     //Make sure we avoid duplicates1038                     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                 else1056                     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 delegates1074                     //The bonus here is if the delegate has not been added we continue quite happily1075                     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                     else1097                         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 types1123         /// </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             try1158             {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 lock1162                 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 types1170                     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 about1184                         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 here1192                     return;1193                 else1194                 {1195                     //Idiot check1196                     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 once1200                     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 delgates1206                     //We need to use a copy because we may modify the original delegate list during processing1207                     foreach (IPacketTypeHandlerDelegateWrapper wrapper in handlersCopy)1208                     {1209                         try1210                         {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 exception1226                 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 options1241             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         #endregion1284 1285         #region Connection Establish and Shutdown1286         /// <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.Shutdown1319         /// </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                 else1333                     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                     else1370                         globalConnectionEstablishDelegatesSync += connectionEstablishDelegate;1371                 }1372                 else1373                 {1374                     if (globalConnectionEstablishDelegatesAsync == null)1375                         globalConnectionEstablishDelegatesAsync = connectionEstablishDelegate;1376                     else1377                         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 delegates1395                 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             try1419             {1420                 CloseAllConnections();1421             }1422             catch (CommsException)1423             {1424 1425             }1426             catch (Exception ex)1427             {1428                 LogError(ex, "CommsShutdownError");1429             }1430 1431 #if !WINDOWS_PHONE && !ANDROID1432             try1433             {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 #endif1449 1450             try1451             {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_LOGGING1465             //Mono bug fix1466             //Sometimes NLog ends up in a deadlock on close, workaround provided on NLog website1467             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 #endif1478         }1479         #endregion1480          1481         #region Timeouts1482         /// <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         #endregion1502 1503         #region Logging1504         /// <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 Logger1515         {1516             get { return logger; }1517         }1518 1519 #if NO_LOGGING1520         /// <summary>1521         /// Enable basic logging using the provided logFileLocation1522         /// </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 NetworkCommsDotNet1536         /// </summary>1537         public static void DisableLogging()1538         {1539             lock (globalDictAndDelegateLocker)1540             {1541                 LoggingEnabled = false;1542                 logger = null;1543             }1544         }1545 #else1546         /// <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 NetworkCommsDotNet1576         /// </summary>1577         public static void DisableLogging()1578         {1579             lock (globalDictAndDelegateLocker)1580             {1581                 LoggingEnabled = false;1582                 LogManager.DisableLogging();1583             }1584         }1585 #endif1586 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             try1600             {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 iOS1628                 //We need to ensure we add the correct document path for iOS1629                 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 ANDROID1631                 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_PHONE1633                 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 #else1635                 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 #endif1638 1639                 if (LoggingEnabled) logger.Fatal(entireFileName, ex);1640 1641                 try1642                 {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         #endregion1673 1674         #region Serializers and Compressors1675         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 values1685         /// </summary>1686         public static SendReceiveOptions DefaultSendReceiveOptions { get; set; }1687         #endregion1688 1689         #region Connection Access1690         /// <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_PHONE1733             new DPSBase.MD5Managed())1734 #else1735             System.Security.Cryptography.MD5.Create())1736 #endif1737             {1738                 //If we don‘t ensure the position is consistent the MD5 changes1739                 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 string1766         /// </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 connections1779         /// </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 connections1864         /// </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 connections1901         /// </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 connections1959         /// </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                 else2044                     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                 else2076                     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 ConnectionType2084         /// </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 ConnectionType2104         /// </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 ConnectionType2130         /// </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 = http://www.mamicode.com/false;2169 2170             //Ensure connection references are removed from networkComms2171             //Once we think we have closed the connection it‘s time to get rid of our other references2172             lock (globalDictAndDelegateLocker)2173             {2174                 #region Update NetworkComms Connection Dictionaries2175                 ShortGuid currentNetworkIdentifier = connection.ConnectionInfo.NetworkIdentifier;2176 2177                 //We establish whether we have already done this step2178                 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 close2186                     returnValue = http://www.mamicode.com/true;2187                 }2188 2189                 //Keep a reference of the connection for possible debugging later2190                 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                         else2197                             oldNetworkIdentifierToConnectionInfo[currentNetworkIdentifier].Add(connection.ConnectionInfo.ConnectionType, new List<ConnectionInfo>() { connection.ConnectionInfo });2198                     }2199                     else2200                         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                     //else2209                     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 empty2213                     if (allConnectionsById[currentNetworkIdentifier][connection.ConnectionInfo.ConnectionType].Count == 0)2214                         allConnectionsById[currentNetworkIdentifier].Remove(connection.ConnectionInfo.ConnectionType);2215 2216                     //Remove the identifier reference2217                     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 well2224                 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 well2230                     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                 #endregion2236             }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 endPoint2253             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 lock2262             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 lock2270                 if (ConnectionExists(endPointToUse, connection.ConnectionInfo.ConnectionType))2271                 {2272                     //If a connection still exist we don‘t assume it is the same as above2273                     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                     else2280                     {2281                         //We have just tried to add the same reference twice, no need to do anything this time around2282                     }2283                 }2284                 else2285                 {2286 #if FREETRIAL2287                     //If this is a free trial we only allow a single connection. We will throw an exception if any connections already exist2288                     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 #endif2291 2292                     //Add reference to the endPoint dictionary2293                     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                         else2298                             allConnectionsByEndPoint[endPointToUse].Add(connection.ConnectionInfo.ConnectionType, connection);2299                     }2300                     else2301                         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 returns2308         /// </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 dictionary2344                 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 worries2348                 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                         else2361                         {2362                             //We are trying to add the same connection twice, so just do nothing here.2363                         }2364                     }2365                     else2366                         allConnectionsById[connection.ConnectionInfo.NetworkIdentifier].Add(connection.ConnectionInfo.ConnectionType, new List<Connection>() { connection });2367                 }2368                 else2369                     allConnectionsById.Add(connection.ConnectionInfo.NetworkIdentifier, new Dictionary<ConnectionType, List<Connection>>() { { connection.ConnectionInfo.ConnectionType, new List<Connection>() {connection}} });2370             }2371         }2372         #endregion2373     }
View Code

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

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

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

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

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

www.cnblogs.com/networkcomms

www.networkcomms.cn(建设中)

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