The Labs \ Source Viewer \ SSCLI \ System.Runtime.Remoting.Channels \ SocketCache

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. //==========================================================================
  16. // File: SocketCache.cs
  17. //
  18. // Summary: Cache for client sockets.
  19. //
  20. //==========================================================================
  21. using System;
  22. using System.Collections;
  23. using System.Net;
  24. using System.Net.Sockets;
  25. using System.Runtime.Remoting;
  26. using System.Runtime.Remoting.Channels;
  27. using System.Threading;
  28. namespace System.Runtime.Remoting.Channels
  29. {
  30.    
  31.     // Delegate to method that will fabricate the appropriate socket handler
  32.     internal delegate SocketHandler SocketHandlerFactory(Socket socket, SocketCache socketCache, string machineAndPort);
  33.    
  34.    
  35.     // Used to cache client connections to a single port on a server
  36.     internal class RemoteConnection
  37.     {
  38.         private static char[] colonSep = new char[] {':'};
  39.        
  40.         private CachedSocketList _cachedSocketList;
  41.        
  42.         // reference back to the socket cache
  43.         private SocketCache _socketCache;
  44.        
  45.         // remote endpoint data
  46.         private string _machineAndPort;
  47.         private IPAddress[] _addressList;
  48.         private int _port;
  49.         private EndPoint _lkgIPEndPoint;
  50.         private bool connectIPv6 = false;
  51.        
  52.        
  53.         internal RemoteConnection(SocketCache socketCache, string machineAndPort)
  54.         {
  55.             _socketCache = socketCache;
  56.            
  57.             _cachedSocketList = new CachedSocketList(socketCache.SocketTimeout, socketCache.CachePolicy);
  58.            
  59.             // parse "machinename:port", add a dummy scheme to get uri parsing
  60.             Uri uri = new Uri("dummy://" + machineAndPort);
  61.             string machineName = uri.Host;
  62.             _port = uri.Port;
  63.            
  64.             _machineAndPort = machineAndPort;
  65.            
  66.             _addressList = Dns.GetHostAddresses(machineName);
  67.             connectIPv6 = Socket.OSSupportsIPv6 && HasIPv6Address(_addressList);
  68.         }
  69.         // RemoteConnection
  70.        
  71.         internal SocketHandler GetSocket()
  72.         {
  73.             // try the cached socket list
  74.             SocketHandler socketHandler = _cachedSocketList.GetSocket();
  75.             if (socketHandler != null)
  76.                 return socketHandler;
  77.            
  78.             // Otherwise, we'll just create a new one.
  79.             return CreateNewSocket();
  80.         }
  81.         // GetSocket
  82.         internal void ReleaseSocket(SocketHandler socket)
  83.         {
  84.             socket.ReleaseControl();
  85.             _cachedSocketList.ReturnSocket(socket);
  86.         }
  87.         // ReleaseSocket
  88.        
  89.         private bool HasIPv6Address(IPAddress[] addressList)
  90.         {
  91.             foreach (IPAddress address in addressList) {
  92.                 if (address.AddressFamily == AddressFamily.InterNetworkV6)
  93.                     return true;
  94.             }
  95.             return false;
  96.         }
  97.        
  98.         private void DisableNagleDelays(Socket socket)
  99.         {
  100.             // disable nagle delays
  101.             socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1);
  102.         }
  103.        
  104.         private SocketHandler CreateNewSocket()
  105.         {
  106.             // If there is only one entry in the list, just use that
  107.             // we will fail if the connect on it fails
  108.             if (_addressList.Length == 1)
  109.                 return CreateNewSocket(new IPEndPoint(_addressList[0], _port));
  110.            
  111.             // If LKG is set try using that
  112.             if (_lkgIPEndPoint != null) {
  113.                 try {
  114.                     return CreateNewSocket(_lkgIPEndPoint);
  115.                 }
  116.                 catch (Exception) {
  117.                     // Since this fail null out LKG
  118.                     _lkgIPEndPoint = null;
  119.                 }
  120.             }
  121.            
  122.             // If IPv6 is enabled try connecting to IP addresses
  123.             if (connectIPv6) {
  124.                 try {
  125.                     return CreateNewSocket(AddressFamily.InterNetworkV6);
  126.                 }
  127.                 catch (Exception) {
  128.                 }
  129.             }
  130.            
  131.             // If everything fails try ipv4 addresses
  132.             return CreateNewSocket(AddressFamily.InterNetwork);
  133.            
  134.         }
  135.        
  136.         private SocketHandler CreateNewSocket(EndPoint ipEndPoint)
  137.         {
  138.             Socket socket = new Socket(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  139.            
  140.             DisableNagleDelays(socket);
  141.             InternalRemotingServices.RemotingTrace("RemoteConnection::CreateNewSocket: connecting new socket :: " + ipEndPoint);
  142.            
  143.             socket.Connect(ipEndPoint);
  144.             _lkgIPEndPoint = socket.RemoteEndPoint;
  145.             return _socketCache.CreateSocketHandler(socket, _machineAndPort);
  146.         }
  147.         // CreateNewSocket
  148.         private SocketHandler CreateNewSocket(AddressFamily family)
  149.         {
  150.             Socket socket = new Socket(family, SocketType.Stream, ProtocolType.Tcp);
  151.            
  152.             DisableNagleDelays(socket);
  153.            
  154.             socket.Connect(_addressList, _port);
  155.             _lkgIPEndPoint = socket.RemoteEndPoint;
  156.             return _socketCache.CreateSocketHandler(socket, _machineAndPort);
  157.         }
  158.         // CreateNewSocket
  159.        
  160.         internal void TimeoutSockets(DateTime currentTime)
  161.         {
  162.             _cachedSocketList.TimeoutSockets(currentTime, _socketCache.SocketTimeout);
  163.         }
  164.         // TimeoutSockets
  165.        
  166.        
  167.     }
  168.     // class RemoteConnection
  169.    
  170.    
  171.     internal class CachedSocket
  172.     {
  173.         private SocketHandler _socket;
  174.         private DateTime _socketLastUsed;
  175.        
  176.         private CachedSocket _next;
  177.        
  178.         internal CachedSocket(SocketHandler socket, CachedSocket next)
  179.         {
  180.             _socket = socket;
  181.             _socketLastUsed = DateTime.UtcNow;
  182.             _next = next;
  183.         }
  184.         // CachedSocket
  185.         internal SocketHandler Handler {
  186.             get { return _socket; }
  187.         }
  188.         internal DateTime LastUsed {
  189.             get { return _socketLastUsed; }
  190.         }
  191.        
  192.         internal CachedSocket Next {
  193.             get { return _next; }
  194.             set { _next = value; }
  195.         }
  196.        
  197.     }
  198.     // class CachedSocket
  199.    
  200.     internal class CachedSocketList
  201.     {
  202.         private int _socketCount;
  203.         private TimeSpan _socketLifetime;
  204.         private SocketCachePolicy _socketCachePolicy;
  205.         private CachedSocket _socketList;
  206.         // linked list
  207.         internal CachedSocketList(TimeSpan socketLifetime, SocketCachePolicy socketCachePolicy)
  208.         {
  209.             _socketCount = 0;
  210.             _socketLifetime = socketLifetime;
  211.             _socketCachePolicy = socketCachePolicy;
  212.             _socketList = null;
  213.         }
  214.         // CachedSocketList
  215.        
  216.         internal SocketHandler GetSocket()
  217.         {
  218.             if (_socketCount == 0)
  219.                 return null;
  220.            
  221.             lock (this) {
  222.                 if (_socketList != null) {
  223.                     SocketHandler socket = _socketList.Handler;
  224.                     _socketList = _socketList.Next;
  225.                    
  226.                     bool bRes = socket.RaceForControl();
  227.                    
  228.                     // We should always have control since there shouldn't
  229.                     // be contention here.
  230.                     InternalRemotingServices.RemotingAssert(bRes, "someone else has the socket?");
  231.                    
  232.                     _socketCount--;
  233.                     return socket;
  234.                 }
  235.             }
  236.            
  237.             return null;
  238.         }
  239.         // GetSocket
  240.        
  241.         internal void ReturnSocket(SocketHandler socket)
  242.         {
  243.             TimeSpan socketActiveTime = DateTime.UtcNow - socket.CreationTime;
  244.             bool closeSocket = false;
  245.             lock (this) {
  246.                 // Check if socket active time has exceeded the lifetime
  247.                 // If it has just close the Socket
  248.                 if (_socketCachePolicy != SocketCachePolicy.AbsoluteTimeout || socketActiveTime < _socketLifetime) {
  249.                     // Ignore duplicate entries for the same socket handler
  250.                     for (CachedSocket curr = _socketList; curr != null; curr = curr.Next) {
  251.                         if (socket == curr.Handler)
  252.                             return;
  253.                     }
  254.                    
  255.                     _socketList = new CachedSocket(socket, _socketList);
  256.                     _socketCount++;
  257.                 }
  258.                 else {
  259.                     closeSocket = true;
  260.                 }
  261.             }
  262.             if (closeSocket) {
  263.                 socket.Close();
  264.             }
  265.         }
  266.         // ReturnSocket
  267.        
  268.         internal void TimeoutSockets(DateTime currentTime, TimeSpan socketLifetime)
  269.         {
  270.             lock (this) {
  271.                 CachedSocket prev = null;
  272.                 CachedSocket curr = _socketList;
  273.                
  274.                 while (curr != null) {
  275.                     // see if it's lifetime has expired
  276.                     // This is for absolute
  277.                     // This is relative behaviour
  278.                     if ((_socketCachePolicy == SocketCachePolicy.AbsoluteTimeout && (currentTime - curr.Handler.CreationTime) > socketLifetime) || (currentTime - curr.LastUsed) > socketLifetime) {
  279.                         curr.Handler.Close();
  280.                        
  281.                         // remove current cached socket from list
  282.                         if (prev == null) {
  283.                             // it's the first item, so update _socketList
  284.                             _socketList = curr.Next;
  285.                             curr = _socketList;
  286.                         }
  287.                         else {
  288.                             // remove current item from the list
  289.                             curr = curr.Next;
  290.                             prev.Next = curr;
  291.                         }
  292.                        
  293.                         // decrement socket count
  294.                         _socketCount--;
  295.                     }
  296.                     else {
  297.                         prev = curr;
  298.                         curr = curr.Next;
  299.                     }
  300.                 }
  301.             }
  302.         }
  303.         // TimeoutSockets
  304.        
  305.     }
  306.     // class CachedSocketList
  307.    
  308.    
  309.    
  310.    
  311.     internal class SocketCache
  312.     {
  313.         // collection of RemoteConnection's.
  314.         private static Hashtable _connections = new Hashtable();
  315.        
  316.         private SocketHandlerFactory _handlerFactory;
  317.        
  318.         // socket timeout data
  319.         private static RegisteredWaitHandle _registeredWaitHandle;
  320.         private static WaitOrTimerCallback _socketTimeoutDelegate;
  321.         private static AutoResetEvent _socketTimeoutWaitHandle;
  322.         private static TimeSpan _socketTimeoutPollTime = TimeSpan.FromSeconds(10);
  323.         private SocketCachePolicy _socketCachePolicy;
  324.         private TimeSpan _socketTimeout;
  325.         private int _receiveTimeout = 0;
  326.        
  327.        
  328.         static SocketCache()
  329.         {
  330.             InitializeSocketTimeoutHandler();
  331.         }
  332.        
  333.         internal SocketCache(SocketHandlerFactory handlerFactory, SocketCachePolicy socketCachePolicy, TimeSpan socketTimeout)
  334.         {
  335.             _handlerFactory = handlerFactory;
  336.             _socketCachePolicy = socketCachePolicy;
  337.             _socketTimeout = socketTimeout;
  338.         }
  339.         // SocketCache
  340.         internal TimeSpan SocketTimeout {
  341.             get { return _socketTimeout; }
  342.             set { _socketTimeout = value; }
  343.         }
  344.         internal int ReceiveTimeout {
  345.             get { return _receiveTimeout; }
  346.             set { _receiveTimeout = value; }
  347.         }
  348.         internal SocketCachePolicy CachePolicy {
  349.             get { return _socketCachePolicy; }
  350.             set { _socketCachePolicy = value; }
  351.         }
  352.        
  353.        
  354.         private static void InitializeSocketTimeoutHandler()
  355.         {
  356.             _socketTimeoutDelegate = new WaitOrTimerCallback(TimeoutSockets);
  357.             _socketTimeoutWaitHandle = new AutoResetEvent(false);
  358.             _registeredWaitHandle = ThreadPool.UnsafeRegisterWaitForSingleObject(_socketTimeoutWaitHandle, _socketTimeoutDelegate, "TcpChannelSocketTimeout", _socketTimeoutPollTime, true);
  359.             // execute only once
  360.         }
  361.         // InitializeSocketTimeoutHandler
  362.         private static void TimeoutSockets(object state, bool wasSignalled)
  363.         {
  364.             DateTime currentTime = DateTime.UtcNow;
  365.            
  366.             lock (_connections) {
  367.                 foreach (DictionaryEntry entry in _connections) {
  368.                     RemoteConnection connection = (RemoteConnection)entry.Value;
  369.                     connection.TimeoutSockets(currentTime);
  370.                 }
  371.             }
  372.            
  373.             _registeredWaitHandle.Unregister(null);
  374.             _registeredWaitHandle = ThreadPool.UnsafeRegisterWaitForSingleObject(_socketTimeoutWaitHandle, _socketTimeoutDelegate, "TcpChannelSocketTimeout", _socketTimeoutPollTime, true);
  375.             // execute only once
  376.         }
  377.         // TimeoutSockets
  378.        
  379.        
  380.         internal SocketHandler CreateSocketHandler(Socket socket, string machineAndPort)
  381.         {
  382.             socket.ReceiveTimeout = _receiveTimeout;
  383.             return _handlerFactory(socket, this, machineAndPort);
  384.         }
  385.        
  386.        
  387.         // The key is expected to of the form "machinename:port"
  388.         public SocketHandler GetSocket(string machinePortAndSid, bool openNew)
  389.         {
  390.             RemoteConnection connection = (RemoteConnection)_connections[machinePortAndSid];
  391.             if (openNew || connection == null) {
  392.                 connection = new RemoteConnection(this, machinePortAndSid);
  393.                
  394.                 // doesn't matter if different RemoteConnection's get written at
  395.                 // the same time (GC will come along and close them).
  396.                 lock (_connections) {
  397.                     _connections[machinePortAndSid] = connection;
  398.                 }
  399.             }
  400.            
  401.             return connection.GetSocket();
  402.         }
  403.         // GetSocket
  404.         public void ReleaseSocket(string machinePortAndSid, SocketHandler socket)
  405.         {
  406.             RemoteConnection connection = (RemoteConnection)_connections[machinePortAndSid];
  407.             if (connection != null) {
  408.                 connection.ReleaseSocket(socket);
  409.             }
  410.             else {
  411.                 // there should have been a connection, so let's just close
  412.                 // this socket.
  413.                 socket.Close();
  414.             }
  415.         }
  416.         // ReleaseSocket
  417.        
  418.     }
  419.     // SocketCache
  420.    
  421.    
  422.    
  423. }
  424. // namespace System.Runtime.Remoting.Channels

Developer Fusion