The Labs \ Source Viewer \ SSCLI \ System.Net.Sockets \ TcpClient

  1. //------------------------------------------------------------------------------
  2. // <copyright file="TCPClient.cs" company="Microsoft">
  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. // </copyright>
  14. //------------------------------------------------------------------------------
  15. namespace System.Net.Sockets
  16. {
  17.    
  18.     using System.Threading;
  19.     using System.Security.Permissions;
  20.    
  21.     /// <devdoc>
  22.     /// <para>The <see cref='System.Net.Sockets.TcpClient'/> class provide TCP services at a higher level
  23.     /// of abstraction than the <see cref='System.Net.Sockets.Socket'/> class. <see cref='System.Net.Sockets.TcpClient'/>
  24.     /// is used to create a Client connection to a remote host.</para>
  25.     /// </devdoc>
  26.     public class TcpClient : IDisposable
  27.     {
  28.        
  29.         Socket m_ClientSocket;
  30.         bool m_Active;
  31.         NetworkStream m_DataStream;
  32.        
  33.         //
  34.         // IPv6: Maintain address family for the client
  35.         //
  36.         AddressFamily m_Family = AddressFamily.InterNetwork;
  37.        
  38.         // specify local IP and port
  39.         /// <devdoc>
  40.         /// <para>
  41.         /// Initializes a new instance of the <see cref='System.Net.Sockets.TcpClient'/>
  42.         /// class with the specified end point.
  43.         /// </para>
  44.         /// </devdoc>
  45.         public TcpClient(IPEndPoint localEP)
  46.         {
  47.             if (Logging.On)
  48.                 Logging.Enter(Logging.Sockets, this, "TcpClient", localEP);
  49.             if (localEP == null) {
  50.                 throw new ArgumentNullException("localEP");
  51.             }
  52.             //
  53.             // IPv6: Establish address family before creating a socket
  54.             //
  55.             m_Family = localEP.AddressFamily;
  56.            
  57.             initialize();
  58.             Client.Bind(localEP);
  59.             if (Logging.On)
  60.                 Logging.Exit(Logging.Sockets, this, "TcpClient", "");
  61.         }
  62.        
  63.         // TcpClient(IPaddress localaddr); // port is arbitrary
  64.         // TcpClient(int outgoingPort); // local IP is arbitrary
  65.        
  66.         // address+port is arbitrary
  67.         /// <devdoc>
  68.         /// <para>
  69.         /// Initializes a new instance of the <see cref='System.Net.Sockets.TcpClient'/> class.
  70.         /// </para>
  71.         /// </devdoc>
  72.         public TcpClient() : this(AddressFamily.InterNetwork)
  73.         {
  74.             if (Logging.On)
  75.                 Logging.Enter(Logging.Sockets, this, "TcpClient", null);
  76.             if (Logging.On)
  77.                 Logging.Exit(Logging.Sockets, this, "TcpClient", null);
  78.         }
  79.        
  80.         /// <devdoc>
  81.         /// <para>
  82.         /// Initializes a new instance of the <see cref='System.Net.Sockets.TcpClient'/> class.
  83.         /// </para>
  84.         /// </devdoc>
  85.         #if COMNET_DISABLEIPV6
  86.         private TcpClient(AddressFamily family)
  87.         {
  88.         }
  89.         #else
  90.         public TcpClient(AddressFamily family)
  91.         {
  92.             #endif
  93.             if (Logging.On)
  94.                 Logging.Enter(Logging.Sockets, this, "TcpClient", family);
  95.             //
  96.             // Validate parameter
  97.             //
  98.             if (family != AddressFamily.InterNetwork && family != AddressFamily.InterNetworkV6) {
  99.                 throw new ArgumentException(SR.GetString(SR.net_protocol_invalid_family, "TCP"), "family");
  100.             }
  101.            
  102.             m_Family = family;
  103.            
  104.             initialize();
  105.             if (Logging.On)
  106.                 Logging.Exit(Logging.Sockets, this, "TcpClient", null);
  107.         }
  108.        
  109.         // bind and connect
  110.         /// <devdoc>
  111.         /// <para>Initializes a new instance of the <see cref='System.Net.Sockets.TcpClient'/> class and connects to the
  112.         /// specified port on the specified host.</para>
  113.         /// </devdoc>
  114.         public TcpClient(string hostname, int port)
  115.         {
  116.             if (Logging.On)
  117.                 Logging.Enter(Logging.Sockets, this, "TcpClient", hostname);
  118.             if (hostname == null) {
  119.                 throw new ArgumentNullException("hostname");
  120.             }
  121.             if (!ValidationHelper.ValidateTcpPort(port)) {
  122.                 throw new ArgumentOutOfRangeException("port");
  123.             }
  124.             //
  125.             // IPv6: Delay creating the client socket until we have
  126.             // performed DNS resolution and know which address
  127.             // families we can use.
  128.             //
  129.             //initialize();
  130.            
  131.             try {
  132.                 Connect(hostname, port);
  133.             }
  134.            
  135.             catch (Exception e) {
  136.                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
  137.                     throw;
  138.                 }
  139.                
  140.                 if (m_ClientSocket != null) {
  141.                     m_ClientSocket.Close();
  142.                 }
  143.                 throw e;
  144.             }
  145.             catch {
  146.                 if (m_ClientSocket != null) {
  147.                     m_ClientSocket.Close();
  148.                 }
  149.                 throw;
  150.             }
  151.            
  152.             if (Logging.On)
  153.                 Logging.Exit(Logging.Sockets, this, "TcpClient", null);
  154.         }
  155.        
  156.         //
  157.         // used by TcpListener.Accept()
  158.         //
  159.         internal TcpClient(Socket acceptedSocket)
  160.         {
  161.             if (Logging.On)
  162.                 Logging.Enter(Logging.Sockets, this, "TcpClient", acceptedSocket);
  163.             Client = acceptedSocket;
  164.             m_Active = true;
  165.             if (Logging.On)
  166.                 Logging.Exit(Logging.Sockets, this, "TcpClient", null);
  167.         }
  168.        
  169.         /// <devdoc>
  170.         /// <para>
  171.         /// Used by the class to provide
  172.         /// the underlying network socket.
  173.         /// </para>
  174.         /// </devdoc>
  175.         public Socket Client {
  176.             get { return m_ClientSocket; }
  177.             set { m_ClientSocket = value; }
  178.         }
  179.        
  180.         /// <devdoc>
  181.         /// <para>
  182.         /// Used by the class to indicate that a connection has been made.
  183.         /// </para>
  184.         /// </devdoc>
  185.         protected bool Active {
  186.             get { return m_Active; }
  187.             set { m_Active = value; }
  188.         }
  189.        
  190.         public int Available {
  191.             get { return m_ClientSocket.Available; }
  192.         }
  193.         public bool Connected {
  194.             get { return m_ClientSocket.Connected; }
  195.         }
  196.         public bool ExclusiveAddressUse {
  197.             get { return m_ClientSocket.ExclusiveAddressUse; }
  198.             set { m_ClientSocket.ExclusiveAddressUse = value; }
  199.         }
  200.         //new
  201.        
  202.        
  203.         /// <devdoc>
  204.         /// <para>
  205.         /// Connects the Client to the specified port on the specified host.
  206.         /// </para>
  207.         /// </devdoc>
  208.         public void Connect(string hostname, int port)
  209.         {
  210.             if (Logging.On)
  211.                 Logging.Enter(Logging.Sockets, this, "Connect", hostname);
  212.             if (m_CleanedUp) {
  213.                 throw new ObjectDisposedException(this.GetType().FullName);
  214.             }
  215.             if (hostname == null) {
  216.                 throw new ArgumentNullException("hostname");
  217.             }
  218.             if (!ValidationHelper.ValidateTcpPort(port)) {
  219.                 throw new ArgumentOutOfRangeException("port");
  220.             }
  221.             //
  222.             // Check for already connected and throw here. This check
  223.             // is not required in the other connect methods as they
  224.             // will throw from WinSock. Here, the situation is more
  225.             // complex since we have to resolve a hostname so it's
  226.             // easier to simply block the request up front.
  227.             //
  228.             if (m_Active) {
  229.                 throw new SocketException(SocketError.IsConnected);
  230.             }
  231.            
  232.             //
  233.             // IPv6: We need to process each of the addresses return from
  234.             // DNS when trying to connect. Use of AddressList[0] is
  235.             // bad form.
  236.             //
  237.            
  238.            
  239.             IPAddress[] addresses = Dns.GetHostAddresses(hostname);
  240.             Exception lastex = null;
  241.             Socket ipv6Socket = null;
  242.             Socket ipv4Socket = null;
  243.            
  244.             try {
  245.                 if (m_ClientSocket == null) {
  246.                     if (Socket.OSSupportsIPv6) {
  247.                         ipv6Socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
  248.                         ipv4Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  249.                     }
  250.                     else {
  251.                         m_ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  252.                     }
  253.                 }
  254.                
  255.                 foreach (IPAddress address in addresses) {
  256.                     try {
  257.                         if (m_ClientSocket == null) {
  258.                             //
  259.                             // We came via the <hostname,port> constructor. Set the
  260.                             // address family appropriately, create the socket and
  261.                             // try to connect.
  262.                             //
  263.                             if (address.AddressFamily == AddressFamily.InterNetwork) {
  264.                                 ipv4Socket.Connect(address, port);
  265.                                 m_ClientSocket = ipv4Socket;
  266.                                 ipv6Socket.Close();
  267.                             }
  268.                             else {
  269.                                 ipv6Socket.Connect(address, port);
  270.                                 m_ClientSocket = ipv6Socket;
  271.                                 ipv4Socket.Close();
  272.                             }
  273.                            
  274.                             m_Family = address.AddressFamily;
  275.                             m_Active = true;
  276.                             break;
  277.                         }
  278.                         else if (address.AddressFamily == m_Family) {
  279.                             //
  280.                             // Only use addresses with a matching family
  281.                             //
  282.                             Connect(new IPEndPoint(address, port));
  283.                             m_Active = true;
  284.                             break;
  285.                         }
  286.                     }
  287.                    
  288.                     catch (Exception ex) {
  289.                         if (ex is ThreadAbortException || ex is StackOverflowException || ex is OutOfMemoryException) {
  290.                             throw;
  291.                         }
  292.                         lastex = ex;
  293.                     }
  294.                 }
  295.             }
  296.            
  297.             catch (Exception ex) {
  298.                 if (ex is ThreadAbortException || ex is StackOverflowException || ex is OutOfMemoryException) {
  299.                     throw;
  300.                 }
  301.                 lastex = ex;
  302.             }
  303.            
  304.             finally {
  305.                
  306.                 //cleanup temp sockets if failed
  307.                 //main socket gets closed when tcpclient gets closed
  308.                
  309.                 //did we connect?
  310.                 if (!m_Active) {
  311.                     if (ipv6Socket != null) {
  312.                         ipv6Socket.Close();
  313.                     }
  314.                    
  315.                     if (ipv4Socket != null) {
  316.                         ipv4Socket.Close();
  317.                     }
  318.                    
  319.                    
  320.                     //
  321.                     // The connect failed - rethrow the last error we had
  322.                     //
  323.                     if (lastex != null)
  324.                         throw lastex;
  325.                     else
  326.                         throw new SocketException(SocketError.NotConnected);
  327.                 }
  328.             }
  329.            
  330.             if (Logging.On)
  331.                 Logging.Exit(Logging.Sockets, this, "Connect", null);
  332.         }
  333.        
  334.         /// <devdoc>
  335.         /// <para>
  336.         /// Connects the Client to the specified port on the specified host.
  337.         /// </para>
  338.         /// </devdoc>
  339.         public void Connect(IPAddress address, int port)
  340.         {
  341.             if (Logging.On)
  342.                 Logging.Enter(Logging.Sockets, this, "Connect", address);
  343.             if (m_CleanedUp) {
  344.                 throw new ObjectDisposedException(this.GetType().FullName);
  345.             }
  346.             if (address == null) {
  347.                 throw new ArgumentNullException("address");
  348.             }
  349.             if (!ValidationHelper.ValidateTcpPort(port)) {
  350.                 throw new ArgumentOutOfRangeException("port");
  351.             }
  352.             IPEndPoint remoteEP = new IPEndPoint(address, port);
  353.             Connect(remoteEP);
  354.             if (Logging.On)
  355.                 Logging.Exit(Logging.Sockets, this, "Connect", null);
  356.         }
  357.        
  358.         /// <devdoc>
  359.         /// <para>
  360.         /// Connect the Client to the specified end point.
  361.         /// </para>
  362.         /// </devdoc>
  363.         public void Connect(IPEndPoint remoteEP)
  364.         {
  365.             if (Logging.On)
  366.                 Logging.Enter(Logging.Sockets, this, "Connect", remoteEP);
  367.             if (m_CleanedUp) {
  368.                 throw new ObjectDisposedException(this.GetType().FullName);
  369.             }
  370.             if (remoteEP == null) {
  371.                 throw new ArgumentNullException("remoteEP");
  372.             }
  373.             Client.Connect(remoteEP);
  374.             m_Active = true;
  375.             if (Logging.On)
  376.                 Logging.Exit(Logging.Sockets, this, "Connect", null);
  377.         }
  378.        
  379.        
  380.        
  381.         //methods
  382.         public void Connect(IPAddress[] ipAddresses, int port)
  383.         {
  384.             if (Logging.On)
  385.                 Logging.Enter(Logging.Sockets, this, "Connect", ipAddresses);
  386.             Client.Connect(ipAddresses, port);
  387.             m_Active = true;
  388.             if (Logging.On)
  389.                 Logging.Exit(Logging.Sockets, this, "Connect", null);
  390.         }
  391.        
  392.        
  393.         [HostProtection(ExternalThreading = true)]
  394.         public IAsyncResult BeginConnect(string host, int port, AsyncCallback requestCallback, object state)
  395.         {
  396.             if (Logging.On)
  397.                 Logging.Enter(Logging.Sockets, this, "BeginConnect", host);
  398.             IAsyncResult result = Client.BeginConnect(host, port, requestCallback, state);
  399.             if (Logging.On)
  400.                 Logging.Exit(Logging.Sockets, this, "BeginConnect", null);
  401.             return result;
  402.         }
  403.        
  404.         [HostProtection(ExternalThreading = true)]
  405.         public IAsyncResult BeginConnect(IPAddress address, int port, AsyncCallback requestCallback, object state)
  406.         {
  407.             if (Logging.On)
  408.                 Logging.Enter(Logging.Sockets, this, "BeginConnect", address);
  409.             IAsyncResult result = Client.BeginConnect(address, port, requestCallback, state);
  410.             if (Logging.On)
  411.                 Logging.Exit(Logging.Sockets, this, "BeginConnect", null);
  412.             return result;
  413.         }
  414.        
  415.         [HostProtection(ExternalThreading = true)]
  416.         public IAsyncResult BeginConnect(IPAddress[] addresses, int port, AsyncCallback requestCallback, object state)
  417.         {
  418.            
  419.             if (Logging.On)
  420.                 Logging.Enter(Logging.Sockets, this, "BeginConnect", addresses);
  421.             IAsyncResult result = Client.BeginConnect(addresses, port, requestCallback, state);
  422.             if (Logging.On)
  423.                 Logging.Exit(Logging.Sockets, this, "BeginConnect", null);
  424.             return result;
  425.         }
  426.        
  427.         public void EndConnect(IAsyncResult asyncResult)
  428.         {
  429.            
  430.             if (Logging.On)
  431.                 Logging.Enter(Logging.Sockets, this, "EndConnect", asyncResult);
  432.             Client.EndConnect(asyncResult);
  433.             m_Active = true;
  434.             if (Logging.On)
  435.                 Logging.Exit(Logging.Sockets, this, "EndConnect", null);
  436.         }
  437.        
  438.        
  439.        
  440.        
  441.        
  442.         /// <devdoc>
  443.         /// <para>
  444.         /// Returns the stream used to read and write data to the
  445.         /// remote host.
  446.         /// </para>
  447.         /// </devdoc>
  448.         public NetworkStream GetStream()
  449.         {
  450.             if (Logging.On)
  451.                 Logging.Enter(Logging.Sockets, this, "GetStream", "");
  452.             if (m_CleanedUp) {
  453.                 throw new ObjectDisposedException(this.GetType().FullName);
  454.             }
  455.             if (!Client.Connected) {
  456.                 throw new InvalidOperationException(SR.GetString(SR.net_notconnected));
  457.             }
  458.             if (m_DataStream == null) {
  459.                 m_DataStream = new NetworkStream(Client, true);
  460.             }
  461.             if (Logging.On)
  462.                 Logging.Exit(Logging.Sockets, this, "GetStream", m_DataStream);
  463.             return m_DataStream;
  464.         }
  465.        
  466.         /// <devdoc>
  467.         /// <para>
  468.         /// Disposes the Tcp connection.
  469.         /// </para>
  470.         /// </devdoc>
  471.         //UEUE
  472.         public void Close()
  473.         {
  474.             if (Logging.On)
  475.                 Logging.Enter(Logging.Sockets, this, "Close", "");
  476.             GlobalLog.Print("TcpClient::Close()");
  477.             ((IDisposable)this).Dispose();
  478.             if (Logging.On)
  479.                 Logging.Exit(Logging.Sockets, this, "Close", "");
  480.         }
  481.        
  482.         private bool m_CleanedUp = false;
  483.        
  484.         protected virtual void Dispose(bool disposing)
  485.         {
  486.             if (Logging.On)
  487.                 Logging.Enter(Logging.Sockets, this, "Dispose", "");
  488.             if (m_CleanedUp) {
  489.                 if (Logging.On)
  490.                     Logging.Exit(Logging.Sockets, this, "Dispose", "");
  491.                 return;
  492.             }
  493.            
  494.             if (disposing) {
  495.                 IDisposable dataStream = m_DataStream;
  496.                 if (dataStream != null) {
  497.                     dataStream.Dispose();
  498.                 }
  499.                 else {
  500.                     //
  501.                     // if the NetworkStream wasn't created, the Socket might
  502.                     // still be there and needs to be closed. In the case in which
  503.                     // we are bound to a local IPEndPoint this will remove the
  504.                     // binding and free up the IPEndPoint for later uses.
  505.                     //
  506.                     Socket chkClientSocket = Client;
  507.                     if (chkClientSocket != null) {
  508.                         try {
  509.                             chkClientSocket.InternalShutdown(SocketShutdown.Both);
  510.                         }
  511.                         finally {
  512.                             chkClientSocket.Close();
  513.                             Client = null;
  514.                         }
  515.                     }
  516.                 }
  517.                
  518.                 GC.SuppressFinalize(this);
  519.             }
  520.            
  521.             m_CleanedUp = true;
  522.             if (Logging.On)
  523.                 Logging.Exit(Logging.Sockets, this, "Dispose", "");
  524.         }
  525.        
  526.         /// <internalonly/>
  527.         void IDisposable.Dispose()
  528.         {
  529.             Dispose(true);
  530.         }
  531.        
  532.         ~TcpClient()
  533.         {
  534.             #if DEBUG
  535.             GlobalLog.SetThreadSource(ThreadKinds.Finalization);
  536.             using (GlobalLog.SetThreadKind(ThreadKinds.System | ThreadKinds.Async)) {
  537.                 #endif
  538.                 Dispose(false);
  539.                 #if DEBUG
  540.             }
  541.             #endif
  542.         }
  543.        
  544.         /// <devdoc>
  545.         /// <para>
  546.         /// Gets or sets the size of the receive buffer in bytes.
  547.         /// </para>
  548.         /// </devdoc>
  549.         public int ReceiveBufferSize {
  550.             get { return numericOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer); }
  551.             set { Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, value); }
  552.         }
  553.        
  554.        
  555.         /// <devdoc>
  556.         /// <para>
  557.         /// Gets or
  558.         /// sets the size of the send buffer in bytes.
  559.         /// </para>
  560.         /// </devdoc>
  561.         public int SendBufferSize {
  562.             get { return numericOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer); }
  563.            
  564.             set { Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, value); }
  565.         }
  566.        
  567.         /// <devdoc>
  568.         /// <para>
  569.         /// Gets or sets the receive time out value of the connection in seconds.
  570.         /// </para>
  571.         /// </devdoc>
  572.         public int ReceiveTimeout {
  573.             get { return numericOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout); }
  574.             set { Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, value); }
  575.         }
  576.        
  577.         /// <devdoc>
  578.         /// <para>
  579.         /// Gets or sets the send time out value of the connection in seconds.
  580.         /// </para>
  581.         /// </devdoc>
  582.         public int SendTimeout {
  583.             get { return numericOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout); }
  584.            
  585.             set { Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, value); }
  586.         }
  587.        
  588.         /// <devdoc>
  589.         /// <para>
  590.         /// Gets or sets the value of the connection's linger option.
  591.         /// </para>
  592.         /// </devdoc>
  593.         public LingerOption LingerState {
  594.             get { return (LingerOption)Client.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger); }
  595.             set { Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, value); }
  596.         }
  597.        
  598.         /// <devdoc>
  599.         /// <para>
  600.         /// Enables or disables delay when send or receive buffers are full.
  601.         /// </para>
  602.         /// </devdoc>
  603.         public bool NoDelay {
  604.             get { return numericOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay) != 0 ? true : false; }
  605.             set { Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, value ? 1 : 0); }
  606.         }
  607.        
  608.         private void initialize()
  609.         {
  610.             //
  611.             // IPv6: Use the address family from the constructor (or Connect method)
  612.             //
  613.             Client = new Socket(m_Family, SocketType.Stream, ProtocolType.Tcp);
  614.             m_Active = false;
  615.         }
  616.        
  617.         private int numericOption(SocketOptionLevel optionLevel, SocketOptionName optionName)
  618.         {
  619.             return (int)Client.GetSocketOption(optionLevel, optionName);
  620.         }
  621.        
  622.     }
  623.     // class TCPClient
  624.    
  625. }
  626. // namespace System.Net.Sockets

Developer Fusion