File: net\System\Net\Sockets\NetworkStream.cs
Project: ndp\fx\src\System.csproj (System)
//------------------------------------------------------------------------------
// <copyright file="NetworkStream.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
namespace System.Net.Sockets {
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Threading;
    using System.Security.Permissions;
    using System.Threading.Tasks;
 
    /// <devdoc>
    ///    <para>
    ///       Provides the underlying stream of data for network access.
    ///    </para>
    /// </devdoc>
    public class NetworkStream : Stream {
        /// <devdoc>
        ///    <para>
        ///       Used by the class to hold the underlying socket the stream uses.
        ///    </para>
        /// </devdoc>
        private Socket    m_StreamSocket;
 
        /// <devdoc>
        ///    <para>
        ///       Used by the class to indicate that the stream is m_Readable.
        ///    </para>
        /// </devdoc>
        private bool      m_Readable;
 
        /// <devdoc>
        ///    <para>
        ///       Used by the class to indicate that the stream is writable.
        ///    </para>
        /// </devdoc>
        private bool      m_Writeable;
 
        private bool      m_OwnsSocket;
 
        /// <devdoc>
        /// <para>Creates a new instance of the <see cref='System.Net.Sockets.NetworkStream'/> without initalization.</para>
        /// </devdoc>
        internal NetworkStream() {
            m_OwnsSocket = true;
        }
 
 
        // Can be constructed directly out of a socket
        /// <devdoc>
        /// <para>Creates a new instance of the <see cref='System.Net.Sockets.NetworkStream'/> class for the specified <see cref='System.Net.Sockets.Socket'/>.</para>
        /// </devdoc>
        public NetworkStream(Socket socket) {
#if DEBUG
            using (GlobalLog.SetThreadKind(ThreadKinds.User)) {
#endif
            if (socket == null) {
                throw new ArgumentNullException("socket");
            }
            InitNetworkStream(socket, FileAccess.ReadWrite);
#if DEBUG
            }
#endif
        }
 
        //UEUE (see FileStream)
        // ownsHandle: true if the file handle will be owned by this NetworkStream instance; otherwise, false.
        public NetworkStream(Socket socket, bool ownsSocket) {
#if DEBUG
            using (GlobalLog.SetThreadKind(ThreadKinds.User)) {
#endif
            if (socket == null) {
                throw new ArgumentNullException("socket");
            }
            InitNetworkStream(socket, FileAccess.ReadWrite);
            m_OwnsSocket = ownsSocket;
#if DEBUG
            }
#endif
        }
 
        internal NetworkStream(NetworkStream networkStream, bool ownsSocket) {
            Socket socket = networkStream.Socket;
            if (socket == null) {
                throw new ArgumentNullException("networkStream");
            }
            InitNetworkStream(socket, FileAccess.ReadWrite);
            m_OwnsSocket = ownsSocket;
        }
 
        // Create with a socket and access mode
        /// <devdoc>
        /// <para>Creates a new instance of the <see cref='System.Net.Sockets.NetworkStream'/> class for the specified <see cref='System.Net.Sockets.Socket'/> with the specified access rights.</para>
        /// </devdoc>
        public NetworkStream(Socket socket, FileAccess access) {
#if DEBUG
            using (GlobalLog.SetThreadKind(ThreadKinds.User)) {
#endif
            if (socket == null) {
                throw new ArgumentNullException("socket");
            }
            InitNetworkStream(socket, access);
#if DEBUG
            }
#endif
        }
        public NetworkStream(Socket socket, FileAccess access, bool ownsSocket) {
#if DEBUG
            using (GlobalLog.SetThreadKind(ThreadKinds.User)) {
#endif
            if (socket == null) {
                throw new ArgumentNullException("socket");
            }
            InitNetworkStream(socket, access);
            m_OwnsSocket = ownsSocket;
#if DEBUG
            }
#endif
        }
 
        //
        // Socket - provides access to socket for stream closing
        //
        protected Socket Socket {
            get {
                return m_StreamSocket;
            }
        }
 
        internal Socket InternalSocket {
            get {
                Socket chkSocket = m_StreamSocket;
                if (m_CleanedUp || chkSocket == null) {
                    throw new ObjectDisposedException(this.GetType().FullName);
                }
 
                return chkSocket;
            }
        }
 
        internal void InternalAbortSocket() 
        {
            if (!m_OwnsSocket)
            {
                throw new InvalidOperationException();
            }
 
            Socket chkSocket = m_StreamSocket;
            if (m_CleanedUp || chkSocket == null)
            {
                return;
            }
 
            try
            {
                chkSocket.Close(0);
            }
            catch (ObjectDisposedException)
            {
            }
        }
 
        internal void ConvertToNotSocketOwner() {
            m_OwnsSocket = false;
            // Suppress for finialization still allow proceed the requests
            GC.SuppressFinalize(this);
        }
 
        /// <devdoc>
        ///    <para>
        ///       Used by the class to indicate that the stream is m_Readable.
        ///    </para>
        /// </devdoc>
        protected bool Readable {
            get {
                return m_Readable;
            }
            set {
                m_Readable = value;
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Used by the class to indicate that the stream is writable.
        ///    </para>
        /// </devdoc>
        protected bool Writeable {
            get {
                return m_Writeable;
            }
            set {
                m_Writeable = value;
            }
        }
 
 
        /// <devdoc>
        ///    <para>
        ///       Indicates that data can be read from the stream.
        ///         We return the readability of this stream. This is a read only property.
        ///    </para>
        /// </devdoc>
        public override bool CanRead {
            get {
                return m_Readable;
            }
        }
 
 
        /// <devdoc>
        ///    <para>
        ///       Indicates that the stream can seek a specific location
        ///       in the stream. This property always returns <see langword='false'/>
        ///       .
        ///    </para>
        /// </devdoc>
        public override bool CanSeek {
            get {
                return false;
            }
        }
 
 
        /// <devdoc>
        ///    <para>
        ///       Indicates that data can be written to the stream.
        ///    </para>
        /// </devdoc>
        public override bool CanWrite {
            get {
                return m_Writeable;
            }
        }
 
 
        /// <devdoc>
        ///    <para>Indicates whether we can timeout</para>
        /// </devdoc>
        public override bool CanTimeout { 
            get {
                return true; // should we check for Connected state?
            }
        }
 
 
        /// <devdoc>
        ///    <para>Set/Get ReadTimeout, note of a strange behavior, 0 timeout == infinite for sockets,
        ///         so we map this to -1, and if you set 0, we cannot support it</para>
        /// </devdoc>
        public override int ReadTimeout { 
            get {
#if DEBUG
                using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Async)) {
#endif
                int timeout = (int)m_StreamSocket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout);
                if (timeout == 0) {
                    return -1;
                }
                return timeout;
#if DEBUG
                }
#endif
            }
            set {
#if DEBUG
                using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Async)) {
#endif
                if (value<=0 && value!=System.Threading.Timeout.Infinite) {
                    throw new ArgumentOutOfRangeException("value", SR.GetString(SR.net_io_timeout_use_gt_zero));
                }
                SetSocketTimeoutOption(SocketShutdown.Receive, value, false);
#if DEBUG
                }
#endif
            }
        }
 
        /// <devdoc>
        ///    <para>Set/Get WriteTimeout, note of a strange behavior, 0 timeout == infinite for sockets,
        ///         so we map this to -1, and if you set 0, we cannot support it</para>
        /// </devdoc>
	    public override int WriteTimeout { 
            get {
#if DEBUG
                using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Async)) {
#endif
                int timeout = (int)m_StreamSocket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout);
                if (timeout == 0) {
                    return -1;
                }
                return timeout;
#if DEBUG
                }
#endif
            }
            set {
#if DEBUG
                using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Async)) {
#endif
                if (value <= 0 && value != System.Threading.Timeout.Infinite) {
                    throw new ArgumentOutOfRangeException("value", SR.GetString(SR.net_io_timeout_use_gt_zero));
                }
                SetSocketTimeoutOption(SocketShutdown.Send, value, false);
#if DEBUG
                }
#endif
            }
        }        
 
        /// <devdoc>
        ///    <para>
        ///       Indicates data is available on the stream to be read.
        ///         This property checks to see if at least one byte of data is currently available            
        ///    </para>
        /// </devdoc>
        public virtual bool DataAvailable {
            get {
#if DEBUG
                using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Async)) {
#endif
                if (m_CleanedUp){
                    throw new ObjectDisposedException(this.GetType().FullName);
                }
 
                Socket chkStreamSocket = m_StreamSocket;
                if(chkStreamSocket == null) {
                    throw new IOException(SR.GetString(SR.net_io_readfailure, SR.GetString(SR.net_io_connectionclosed)));
                }
 
                // Ask the socket how many bytes are available. If it's
                // not zero, return true.
 
                return chkStreamSocket.Available != 0;
#if DEBUG
                }
#endif
            }
        }
 
 
        /// <devdoc>
        ///    <para>
        ///       The length of data available on the stream. Always throws <see cref='NotSupportedException'/>.
        ///    </para>
        /// </devdoc>
        public override long Length {
            get {
                throw new NotSupportedException(SR.GetString(SR.net_noseek));
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the position in the stream. Always throws <see cref='NotSupportedException'/>.
        ///    </para>
        /// </devdoc>
        public override long Position {
            get {
                throw new NotSupportedException(SR.GetString(SR.net_noseek));
            }
 
            set {
                throw new NotSupportedException(SR.GetString(SR.net_noseek));
            }
        }
 
 
        /// <devdoc>
        ///    <para>
        ///       Seeks a specific position in the stream. This method is not supported by the
        ///    <see cref='NetworkStream'/> class.
        ///    </para>
        /// </devdoc>
        public override long Seek(long offset, SeekOrigin origin) {
            throw new NotSupportedException(SR.GetString(SR.net_noseek));
        }
 
 
        /*++
 
            InitNetworkStream - initialize a network stream.
 
            This is the common NetworkStream constructor, called whenever a
            network stream is created. We validate the socket, set a few
            options, and call our parent's initializer.
 
            Input:
 
                S           - Socket to be used.
                Access      - Access type desired.
 
 
            Returns:
 
                Nothing, but may throw an exception.
        --*/
 
        internal void InitNetworkStream(Socket socket, FileAccess Access) {
            //
            // parameter validation
            //
            if (!socket.Blocking) {
                throw new IOException(SR.GetString(SR.net_sockets_blocking));
            }
            if (!socket.Connected) {
                throw new IOException(SR.GetString(SR.net_notconnected));
            }
            if (socket.SocketType != SocketType.Stream) {
                throw new IOException(SR.GetString(SR.net_notstream));
            }
 
            m_StreamSocket = socket;
 
            switch (Access) {
                case FileAccess.Read:
                    m_Readable = true;
                    break;
                case FileAccess.Write:
                    m_Writeable = true;
                    break;
                case FileAccess.ReadWrite:
                default: // assume FileAccess.ReadWrite
                    m_Readable = true;
                    m_Writeable = true;
                    break;
            }
 
        }
 
        internal bool PollRead() {
            if (m_CleanedUp) {
                return false;
            }
            Socket chkStreamSocket = m_StreamSocket;
            if (chkStreamSocket == null) {
                return false;
            }
            return chkStreamSocket.Poll(0, SelectMode.SelectRead);
        }
 
        internal bool Poll(int microSeconds, SelectMode mode) {
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
 
            Socket chkStreamSocket = m_StreamSocket;
            if (chkStreamSocket == null) {
                throw new IOException(SR.GetString(SR.net_io_readfailure, SR.GetString(SR.net_io_connectionclosed)));
            }
 
            return chkStreamSocket.Poll(microSeconds, mode);
        }
 
 
        /*++
            Read - provide core Read functionality.
 
            Provide core read functionality. All we do is call through to the
            socket Receive functionality.
 
            Input:
 
                Buffer  - Buffer to read into.
                Offset  - Offset into the buffer where we're to read.
                Count   - Number of bytes to read.
 
            Returns:
 
                Number of bytes we read, or 0 if the socket is closed.
 
        --*/
 
        /// <devdoc>
        ///    <para>
        ///       Reads data from the stream.
        ///    </para>
        /// </devdoc>
        //UEUE
        public override int Read([In, Out] byte[] buffer, int offset, int size) {
#if DEBUG
            using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Sync)) {
#endif
            bool canRead = CanRead;  // Prevent ---- with Dispose.
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
            if (!canRead) {    
                throw new InvalidOperationException(SR.GetString(SR.net_writeonlystream));
            }
            //
            // parameter validation
            //
            if (buffer==null) {
                throw new ArgumentNullException("buffer");
            }
            if (offset<0 || offset>buffer.Length) {
                throw new ArgumentOutOfRangeException("offset");
            }
            if (size<0 || size>buffer.Length-offset) {
                throw new ArgumentOutOfRangeException("size");
            }
 
 
            Socket chkStreamSocket = m_StreamSocket;
            if (chkStreamSocket == null) {
                throw new IOException(SR.GetString(SR.net_io_readfailure, SR.GetString(SR.net_io_connectionclosed)));
            }
 
            try {
                int bytesTransferred = chkStreamSocket.Receive(buffer, offset, size, 0);
                return bytesTransferred;
            }
            catch (Exception exception) {
                if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {                                       
                    throw;
	            }
 
                //
                // some sort of error occured on the socket call,
                // set the SocketException as InnerException and throw
                //
                throw new IOException(SR.GetString(SR.net_io_readfailure, exception.Message), exception);
            }
#if DEBUG
            }
#endif
        }
 
        /*++
            Write - provide core Write functionality.
 
            Provide core write functionality. All we do is call through to the
            socket Send method..
 
            Input:
 
                Buffer  - Buffer to write from.
                Offset  - Offset into the buffer from where we'll start writing.
                Count   - Number of bytes to write.
 
            Returns:
 
                Number of bytes written. We'll throw an exception if we
                can't write everything. It's brutal, but there's no other
                way to indicate an error.
        --*/
 
        /// <devdoc>
        ///    <para>
        ///       Writes data to the stream..
        ///    </para>
        /// </devdoc>
        public override void Write(byte[] buffer, int offset, int size) {
#if DEBUG
            using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Sync)) {
#endif
            bool canWrite = CanWrite; // Prevent ---- with Dispose.
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
            if (!canWrite) {    
                throw new InvalidOperationException(SR.GetString(SR.net_readonlystream));
            }
            //
            // parameter validation
            //
            if (buffer==null) {
                throw new ArgumentNullException("buffer");
            }
            if (offset<0 || offset>buffer.Length) {
                throw new ArgumentOutOfRangeException("offset");
            }
            if (size<0 || size>buffer.Length-offset) {
                throw new ArgumentOutOfRangeException("size");
            }
 
 
            Socket chkStreamSocket = m_StreamSocket;
            if(chkStreamSocket == null) {
                throw new IOException(SR.GetString(SR.net_io_writefailure, SR.GetString(SR.net_io_connectionclosed)));
            }
 
            try {
                //
                // since the socket is in blocking mode this will always complete
                // after ALL the requested number of bytes was transferred
                //
                chkStreamSocket.Send(buffer, offset, size, SocketFlags.None);
            }
            catch (Exception exception) {
                if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {                                       
                    throw;
	            }
 
                //
                // some sort of error occured on the socket call,
                // set the SocketException as InnerException and throw
                //
                throw new IOException(SR.GetString(SR.net_io_writefailure, exception.Message), exception);
            }
#if DEBUG
            }
#endif
        }
 
        private int m_CloseTimeout = Socket.DefaultCloseTimeout; // 1 ms; -1 = respect linger options
 
        public void Close(int timeout) {
#if DEBUG
            using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Sync)) {
#endif
            if (timeout < -1) {
                throw new ArgumentOutOfRangeException("timeout");
            }
            m_CloseTimeout = timeout;
            Close();
#if DEBUG
            }
#endif
        }
 
        private volatile bool m_CleanedUp = false;
        protected override void Dispose(bool disposing) {
#if DEBUG
            using (GlobalLog.SetThreadKind(ThreadKinds.User)) {
#endif
            // Mark this as disposed before changing anything else.
            bool cleanedUp = m_CleanedUp;
            m_CleanedUp = true; 
            if (!cleanedUp && disposing) {
                //
                // only resource we need to free is the network stream, since this
                // is based on the client socket, closing the stream will cause us
                // to flush the data to the network, close the stream and (in the
                // NetoworkStream code) close the socket as well.
                //
                if (m_StreamSocket!=null) {
                    m_Readable = false;
                    m_Writeable = false;
                    if (m_OwnsSocket) {
                        //
                        // if we own the Socket (false by default), close it
                        // ignoring possible exceptions (eg: the user told us
                        // that we own the Socket but it closed at some point of time,
                        // here we would get an ObjectDisposedException)
                        //
                        Socket chkStreamSocket = m_StreamSocket;
                        if (chkStreamSocket!=null) {
                            chkStreamSocket.InternalShutdown(SocketShutdown.Both);
                            chkStreamSocket.Close(m_CloseTimeout);
                        }
                    }
                }
            }
#if DEBUG
            }
#endif
            base.Dispose(disposing);
        }
 
        ~NetworkStream() {
#if DEBUG
            GlobalLog.SetThreadSource(ThreadKinds.Finalization);
           // using (GlobalLog.SetThreadKind(ThreadKinds.System | ThreadKinds.Async)) {
#endif
            Dispose(false);
#if DEBUG
           // }
#endif
        }
 
        /// <devdoc>
        ///    <para>
        ///       Indicates whether the stream is still connected
        ///    </para>
        /// </devdoc>
        internal bool Connected {
            get {
                Socket socket = m_StreamSocket;
                if (!m_CleanedUp && socket !=null && socket.Connected) {
                    return true;
                } else {
                    return false;
                }
            }
        }
 
 
        /*++
            BeginRead - provide async read functionality.
 
            This method provides async read functionality. All we do is
            call through to the underlying socket async read.
 
            Input:
 
                buffer  - Buffer to read into.
                offset  - Offset into the buffer where we're to read.
                size   - Number of bytes to read.
 
            Returns:
 
                An IASyncResult, representing the read.
 
        --*/
 
        /// <devdoc>
        ///    <para>
        ///       Begins an asychronous read from a stream.
        ///    </para>
        /// </devdoc>
        [HostProtection(ExternalThreading=true)]
        public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, Object state) {
#if DEBUG
            using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Async)) {
#endif
            bool canRead = CanRead; // Prevent ---- with Dispose.
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
            if (!canRead) {    
                throw new InvalidOperationException(SR.GetString(SR.net_writeonlystream));
            }
            //
            // parameter validation
            //
            if (buffer==null) {
                throw new ArgumentNullException("buffer");
            }
            if (offset<0 || offset>buffer.Length) {
                throw new ArgumentOutOfRangeException("offset");
            }
            if (size<0 || size>buffer.Length-offset) {
                throw new ArgumentOutOfRangeException("size");
            }
 
            Socket chkStreamSocket = m_StreamSocket;
            if(chkStreamSocket == null) {
                throw new IOException(SR.GetString(SR.net_io_readfailure, SR.GetString(SR.net_io_connectionclosed)));
            }
 
            try {
                IAsyncResult asyncResult =
                    chkStreamSocket.BeginReceive(
                        buffer,
                        offset,
                        size,
                        SocketFlags.None,
                        callback,
                        state);
 
                return asyncResult;
            }
            catch (Exception exception) {
                if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {                                       
                    throw;
	            }
 
                //
                // some sort of error occured on the socket call,
                // set the SocketException as InnerException and throw
                //
                throw new IOException(SR.GetString(SR.net_io_readfailure, exception.Message), exception);
            }
#if DEBUG
            }
#endif
        }
 
        internal virtual IAsyncResult UnsafeBeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, Object state)
        {
            bool canRead = CanRead; // Prevent ---- with Dispose.
            if (m_CleanedUp)
            {
                throw new ObjectDisposedException(GetType().FullName);
            }
            if (!canRead)
            {
                throw new InvalidOperationException(SR.GetString(SR.net_writeonlystream));
            }
 
            Socket chkStreamSocket = m_StreamSocket;
            if (chkStreamSocket == null)
            {
                throw new IOException(SR.GetString(SR.net_io_readfailure, SR.GetString(SR.net_io_connectionclosed)));
            }
 
            try
            {
                IAsyncResult asyncResult = chkStreamSocket.UnsafeBeginReceive(
                    buffer,
                    offset,
                    size,
                    SocketFlags.None,
                    callback,
                    state);
 
                return asyncResult;
            }
            catch (Exception exception)
            {
                if (NclUtilities.IsFatal(exception)) throw;
 
                //
                // some sort of error occured on the socket call,
                // set the SocketException as InnerException and throw
                //
                throw new IOException(SR.GetString(SR.net_io_readfailure, exception.Message), exception);
            }
        }
 
        /*++
            EndRead - handle the end of an async read.
 
            This method is called when an async read is completed. All we
            do is call through to the core socket EndReceive functionality.
            Input:
 
                buffer  - Buffer to read into.
                offset  - Offset into the buffer where we're to read.
                size   - Number of bytes to read.
 
            Returns:
 
                The number of bytes read. May throw an exception.
 
        --*/
 
        /// <devdoc>
        ///    <para>
        ///       Handle the end of an asynchronous read.
        ///    </para>
        /// </devdoc>
        public override int EndRead(IAsyncResult asyncResult) {
#if DEBUG
            using (GlobalLog.SetThreadKind(ThreadKinds.User)) {
#endif
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
 
            //
            // parameter validation
            //
            if (asyncResult==null) {
                throw new ArgumentNullException("asyncResult");
            }
 
            Socket chkStreamSocket = m_StreamSocket;
            if(chkStreamSocket == null) {
                throw new IOException(SR.GetString(SR.net_io_readfailure, SR.GetString(SR.net_io_connectionclosed)));
            }
 
            try {
                int bytesTransferred = chkStreamSocket.EndReceive(asyncResult);
                return bytesTransferred;
            }
            catch (Exception exception) {
                if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {                                       
                    throw;
	            }
 
                //
                // some sort of error occured on the socket call,
                // set the SocketException as InnerException and throw
                //
                throw new IOException(SR.GetString(SR.net_io_readfailure, exception.Message), exception);
            }
#if DEBUG
            }
#endif
        }
 
        /*++
            BeginWrite - provide async write functionality.
 
            This method provides async write functionality. All we do is
            call through to the underlying socket async send.
 
            Input:
 
                buffer  - Buffer to write into.
                offset  - Offset into the buffer where we're to write.
                size   - Number of bytes to written.
 
            Returns:
 
                An IASyncResult, representing the write.
 
        --*/
 
        /// <devdoc>
        ///    <para>
        ///       Begins an asynchronous write to a stream.
        ///    </para>
        /// </devdoc>
        [HostProtection(ExternalThreading=true)]
        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, Object state) {
#if DEBUG
            using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Async)) {
#endif
            bool canWrite = CanWrite; // Prevent ---- with Dispose.
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
            if (!canWrite) {    
                throw new InvalidOperationException(SR.GetString(SR.net_readonlystream));
            }
            //
            // parameter validation
            //
            if (buffer==null) {
                throw new ArgumentNullException("buffer");
            }
            if (offset<0 || offset>buffer.Length) {
                throw new ArgumentOutOfRangeException("offset");
            }
            if (size<0 || size>buffer.Length-offset) {
                throw new ArgumentOutOfRangeException("size");
            }
 
            Socket chkStreamSocket = m_StreamSocket;
            if(chkStreamSocket == null) {
                throw new IOException(SR.GetString(SR.net_io_writefailure, SR.GetString(SR.net_io_connectionclosed)));
            }
 
            try {
                //
                // call BeginSend on the Socket.
                //
                IAsyncResult asyncResult =
                    chkStreamSocket.BeginSend(
                        buffer,
                        offset,
                        size,
                        SocketFlags.None,
                        callback,
                        state);
 
                return asyncResult;
            }
            catch (Exception exception) {
                if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {                                       
                    throw;
	            }
 
                //
                // some sort of error occured on the socket call,
                // set the SocketException as InnerException and throw
                //
                throw new IOException(SR.GetString(SR.net_io_writefailure, exception.Message), exception);
            }
#if DEBUG
            }
#endif
        }
 
               
 
        internal virtual IAsyncResult UnsafeBeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, Object state) {
#if DEBUG
            using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Async)) {
#endif
                bool canWrite = CanWrite; // Prevent ---- with Dispose.
                if (m_CleanedUp){
                    throw new ObjectDisposedException(this.GetType().FullName);
                }
                
                if (!canWrite) {    
                    throw new InvalidOperationException(SR.GetString(SR.net_readonlystream));
                }
    
                Socket chkStreamSocket = m_StreamSocket;
                if(chkStreamSocket == null) {
                    throw new IOException(SR.GetString(SR.net_io_writefailure, SR.GetString(SR.net_io_connectionclosed)));
                }
    
                try {
                    //
                    // call BeginSend on the Socket.
                    //
                    IAsyncResult asyncResult =
                        chkStreamSocket.UnsafeBeginSend(
                            buffer,
                            offset,
                            size,
                            SocketFlags.None,
                            callback,
                            state);
    
                    return asyncResult;
                }
                catch (Exception exception) {
                    if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {                                       
                        throw;
    	            }
    
                    //
                    // some sort of error occured on the socket call,
                    // set the SocketException as InnerException and throw
                    //
                    throw new IOException(SR.GetString(SR.net_io_writefailure, exception.Message), exception);
                }
#if DEBUG
            }
#endif
        }
 
 
 
        /// <devdoc>
        ///    <para>
        ///       Handle the end of an asynchronous write.
        ///       This method is called when an async write is completed. All we
        ///       do is call through to the core socket EndSend functionality.
        ///       Returns:  The number of bytes read. May throw an exception.
        ///    </para>
        /// </devdoc>
        public override void EndWrite(IAsyncResult asyncResult) {
#if DEBUG
            using (GlobalLog.SetThreadKind(ThreadKinds.User)) {
#endif
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
 
            //
            // parameter validation
            //
            if (asyncResult==null) {
                throw new ArgumentNullException("asyncResult");
            }
 
            Socket chkStreamSocket = m_StreamSocket;
            if(chkStreamSocket == null) {
                throw new IOException(SR.GetString(SR.net_io_writefailure, SR.GetString(SR.net_io_connectionclosed)));
            }
 
            try {
                chkStreamSocket.EndSend(asyncResult);
            }
            catch (Exception exception) {
                if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {                                       
                    throw;
	            }
 
                //
                // some sort of error occured on the socket call,
                // set the SocketException as InnerException and throw
                //
                throw new IOException(SR.GetString(SR.net_io_writefailure, exception.Message), exception);
            }
#if DEBUG
            }
#endif
        }
 
 
        /// <devdoc>
        ///    <para>
        ///       Performs a sync Write of an array of buffers.
        ///    </para>
        /// </devdoc>
        internal virtual void MultipleWrite(BufferOffsetSize[] buffers)
        {
            GlobalLog.ThreadContract(ThreadKinds.Sync, "NetworkStream#" + ValidationHelper.HashString(this) + "::MultipleWrite");
 
            //
            // parameter validation
            //
            if (buffers == null) {
                throw new ArgumentNullException("buffers");
            }
 
            Socket chkStreamSocket = m_StreamSocket;
            if(chkStreamSocket == null) {
                throw new IOException(SR.GetString(SR.net_io_writefailure, SR.GetString(SR.net_io_connectionclosed)));
            }
 
            try {
 
                chkStreamSocket.MultipleSend(
                    buffers,
                    SocketFlags.None);
 
            }
            catch (Exception exception) {
                if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {                                       
                    throw;
	            }
 
                //
                // some sort of error occured on the socket call,
                // set the SocketException as InnerException and throw
                //
                throw new IOException(SR.GetString(SR.net_io_writefailure, exception.Message), exception);
            }
        }
 
 
        /// <devdoc>
        ///    <para>
        ///       Starts off an async Write of an array of buffers.
        ///    </para>
        /// </devdoc>
        internal virtual IAsyncResult BeginMultipleWrite(
            BufferOffsetSize[] buffers,
            AsyncCallback callback,
            Object state)
        {
#if DEBUG
            GlobalLog.ThreadContract(ThreadKinds.Unknown, "NetworkStream#" + ValidationHelper.HashString(this) + "::BeginMultipleWrite");
            using (GlobalLog.SetThreadKind(ThreadKinds.Async)) {
#endif
 
            //
            // parameter validation
            //
            if (buffers == null) {
                throw new ArgumentNullException("buffers");
            }
 
            Socket chkStreamSocket = m_StreamSocket;
            if(chkStreamSocket == null) {
                throw new IOException(SR.GetString(SR.net_io_writefailure, SR.GetString(SR.net_io_connectionclosed)));
            }
 
            try {
 
                //
                // call BeginMultipleSend on the Socket.
                //
                IAsyncResult asyncResult =
                    chkStreamSocket.BeginMultipleSend(
                        buffers,
                        SocketFlags.None,
                        callback,
                        state);
 
                return asyncResult;
            }
            catch (Exception exception) {
 
                if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {                                       
                    throw;
	            }
 
                //
                // some sort of error occured on the socket call,
                // set the SocketException as InnerException and throw
                //
                throw new IOException(SR.GetString(SR.net_io_writefailure, exception.Message), exception);
            }
#if DEBUG
            }
#endif
        }
 
 
        internal virtual IAsyncResult UnsafeBeginMultipleWrite(
            BufferOffsetSize[] buffers,
            AsyncCallback callback,
            Object state)
        {
#if DEBUG
            GlobalLog.ThreadContract(ThreadKinds.Unknown, "NetworkStream#" + ValidationHelper.HashString(this) + "::BeginMultipleWrite");
            using (GlobalLog.SetThreadKind(ThreadKinds.Async)) {
#endif
 
            //
            // parameter validation
            //
            if (buffers == null) {
                throw new ArgumentNullException("buffers");
            }
 
            Socket chkStreamSocket = m_StreamSocket;
            if(chkStreamSocket == null) {
                throw new IOException(SR.GetString(SR.net_io_writefailure, SR.GetString(SR.net_io_connectionclosed)));
            }
 
            try {
 
                //
                // call BeginMultipleSend on the Socket.
                //
                IAsyncResult asyncResult =
                    chkStreamSocket.UnsafeBeginMultipleSend(
                        buffers,
                        SocketFlags.None,
                        callback,
                        state);
 
                return asyncResult;
            }
            catch (Exception exception) {
 
                if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {                                       
                    throw;
	            }
 
                //
                // some sort of error occured on the socket call,
                // set the SocketException as InnerException and throw
                //
                throw new IOException(SR.GetString(SR.net_io_writefailure, exception.Message), exception);
            }
#if DEBUG
            }
#endif
        }
 
 
        internal virtual void EndMultipleWrite(IAsyncResult asyncResult) {
            GlobalLog.ThreadContract(ThreadKinds.Unknown, "NetworkStream#" + ValidationHelper.HashString(this) + "::EndMultipleWrite");
 
            //
            // parameter validation
            //
            if (asyncResult == null) {
                throw new ArgumentNullException("asyncResult");
            }
 
            Socket chkStreamSocket = m_StreamSocket;
            if(chkStreamSocket == null) {
                throw new IOException(SR.GetString(SR.net_io_writefailure, SR.GetString(SR.net_io_connectionclosed)));
            }
 
            try {
                chkStreamSocket.EndMultipleSend(asyncResult);
            }
            catch (Exception exception) {
                if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {                                       
                    throw;
	            }
 
                //
                // some sort of error occured on the socket call,
                // set the SocketException as InnerException and throw
                //
                throw new IOException(SR.GetString(SR.net_io_writefailure, exception.Message), exception);
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Flushes data from the stream.  This is meaningless for us, so it does nothing.
        ///    </para>
        /// </devdoc>
        public override void Flush() {
        }
 
        public override Task FlushAsync(CancellationToken cancellationToken)
        {
            return Task.CompletedTask;
        }
 
        /// <devdoc>
        ///    <para>
        ///       Sets the length of the stream. Always throws <see cref='NotSupportedException'/>
        ///    </para>
        /// </devdoc>
        public override void SetLength(long value) {
            throw new NotSupportedException(SR.GetString(SR.net_noseek));
        }
 
        int m_CurrentReadTimeout = -1;
        int m_CurrentWriteTimeout = -1;
        internal void SetSocketTimeoutOption(SocketShutdown mode, int timeout, bool silent) {
            GlobalLog.Print("NetworkStream#" + ValidationHelper.HashString(this) + "::SetSocketTimeoutOption() mode:" + mode + " silent:" + silent + " timeout:" + timeout + " m_CurrentReadTimeout:" + m_CurrentReadTimeout + " m_CurrentWriteTimeout:" + m_CurrentWriteTimeout);
            GlobalLog.ThreadContract(ThreadKinds.Unknown, "NetworkStream#" + ValidationHelper.HashString(this) + "::SetSocketTimeoutOption");
 
            if (timeout < 0) {
                timeout = 0; // -1 becomes 0 for the winsock stack
            }
 
            Socket chkStreamSocket = m_StreamSocket;
            if (chkStreamSocket==null) {
                return;
            }
            if (mode==SocketShutdown.Send || mode==SocketShutdown.Both) {
                if (timeout!=m_CurrentWriteTimeout) {
                    chkStreamSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, timeout, silent);
                    m_CurrentWriteTimeout = timeout;
                }
            }
            if (mode==SocketShutdown.Receive || mode==SocketShutdown.Both) {
                if (timeout!=m_CurrentReadTimeout) {
                    chkStreamSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, timeout, silent);
                    m_CurrentReadTimeout = timeout;
                }
            }
        }
 
#if TRAVE
        [System.Diagnostics.Conditional("TRAVE")]
        internal void DebugMembers() {
            if (m_StreamSocket != null) {
                GlobalLog.Print("m_StreamSocket:");
                m_StreamSocket.DebugMembers();
            }
        }
#endif
    }
}