Socket.SendFileAsync based on SendPacketsAsync (#52208)
authorGünther Foidl <gue@korporal.at>
Thu, 6 May 2021 15:44:25 +0000 (17:44 +0200)
committerGitHub <noreply@github.com>
Thu, 6 May 2021 15:44:25 +0000 (08:44 -0700)
* Socket.SendFileAsync layered on top of SendPacketsAsync

* Tests

* Cleanup

* Set SendPacketsFlags

* Check if the socket is connection orientated

Cf. https://github.com/dotnet/runtime/pull/52208#discussion_r625922176

* Try to re-use the SendPacketsElement-array

Cf. https://github.com/dotnet/runtime/pull/52208#discussion_r625346014

* Fixed test

Cf. https://github.com/dotnet/runtime/pull/52208#discussion_r625316709

* Update src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs

12 files changed:
src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs
src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj
src/libraries/System.Net.Sockets/src/System/Net/Sockets/SendPacketsElement.cs
src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs
src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs
src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs
src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs
src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs
src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs
src/libraries/System.Net.Sockets/src/System/Net/Sockets/TransmitFileAsyncResult.Windows.cs [deleted file]
src/libraries/System.Net.Sockets/tests/FunctionalTests/SendFile.cs
src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs

index b96ce3a..68b963f 100644 (file)
@@ -412,6 +412,8 @@ namespace System.Net.Sockets
         public void SendFile(string? fileName) { }
         public void SendFile(string? fileName, byte[]? preBuffer, byte[]? postBuffer, System.Net.Sockets.TransmitFileOptions flags) { }
         public void SendFile(string? fileName, System.ReadOnlySpan<byte> preBuffer, System.ReadOnlySpan<byte> postBuffer, System.Net.Sockets.TransmitFileOptions flags) { }
+        public System.Threading.Tasks.ValueTask SendFileAsync(string? fileName, System.ReadOnlyMemory<byte> preBuffer, System.ReadOnlyMemory<byte> postBuffer, System.Net.Sockets.TransmitFileOptions flags, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+        public System.Threading.Tasks.ValueTask SendFileAsync(string? fileName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
         public bool SendPacketsAsync(System.Net.Sockets.SocketAsyncEventArgs e) { throw null; }
         public int SendTo(byte[] buffer, int offset, int size, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEP) { throw null; }
         public int SendTo(byte[] buffer, int size, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEP) { throw null; }
index c576e1b..071580a 100644 (file)
@@ -95,7 +95,6 @@
     <Compile Include="System\Net\Sockets\SocketAsyncEventArgs.Windows.cs" />
     <Compile Include="System\Net\Sockets\IOControlKeepAlive.Windows.cs" />
     <Compile Include="System\Net\Sockets\SocketPal.Windows.cs" />
-    <Compile Include="System\Net\Sockets\TransmitFileAsyncResult.Windows.cs" />
     <Compile Include="System\Net\Sockets\UnixDomainSocketEndPoint.Windows.cs" />
     <Compile Include="$(CommonPath)System\Net\ContextAwareResult.Windows.cs"
              Link="Common\System\Net\ContextAwareResult.Windows.cs" />
index 028b1b4..f528bf0 100644 (file)
@@ -102,7 +102,7 @@ namespace System.Net.Sockets
                 throw new ArgumentOutOfRangeException(nameof(count));
             }
 
-            Initialize(null, null, buffer, new ReadOnlyMemory<byte>(buffer, offset, count), offset, count, endOfPacket);
+            Initialize(null, null, buffer, buffer.AsMemory(offset, count), offset, count, endOfPacket);
         }
 
         public SendPacketsElement(ReadOnlyMemory<byte> buffer) :
index ac604eb..ae67f70 100644 (file)
@@ -555,6 +555,95 @@ namespace System.Net.Sockets
             return saea.SendToAsync(this, cancellationToken);
         }
 
+        /// <summary>
+        /// Sends the file <paramref name="fileName"/> to a connected <see cref="Socket"/> object.
+        /// </summary>
+        /// <param name="fileName">A <see cref="string"/> that contains the path and name of the file to be sent. This parameter can be <see langword="null"/>.</param>
+        /// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
+        /// <exception cref="ObjectDisposedException">The <see cref="Socket"/> object has been closed.</exception>
+        /// <exception cref="NotSupportedException">The <see cref="Socket"/> object is not connected to a remote host.</exception>
+        /// <exception cref="FileNotFoundException">The file <paramref name="fileName"/> was not found.</exception>
+        /// <exception cref="SocketException">An error occurred when attempting to access the socket.</exception>
+        public ValueTask SendFileAsync(string? fileName, CancellationToken cancellationToken = default)
+        {
+            return SendFileAsync(fileName, default, default, TransmitFileOptions.UseDefaultWorkerThread, cancellationToken);
+        }
+
+        /// <summary>
+        /// Sends the file <paramref name="fileName"/> and buffers of data to a connected <see cref="Socket"/> object
+        /// using the specified <see cref="TransmitFileOptions"/> value.
+        /// </summary>
+        /// <param name="fileName">A <see cref="string"/> that contains the path and name of the file to be sent. This parameter can be <see langword="null"/>.</param>
+        /// <param name="preBuffer">A <see cref="byte"/> array that contains data to be sent before the file is sent. This parameter can be <see langword="null"/>.</param>
+        /// <param name="postBuffer">A <see cref="byte"/> array that contains data to be sent after the file is sent. This parameter can be <see langword="null"/>.</param>
+        /// <param name="flags">One or more of <see cref="TransmitFileOptions"/> values.</param>
+        /// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
+        /// <exception cref="ObjectDisposedException">The <see cref="Socket"/> object has been closed.</exception>
+        /// <exception cref="NotSupportedException">The <see cref="Socket"/> object is not connected to a remote host.</exception>
+        /// <exception cref="FileNotFoundException">The file <paramref name="fileName"/> was not found.</exception>
+        /// <exception cref="SocketException">An error occurred when attempting to access the socket.</exception>
+        public ValueTask SendFileAsync(string? fileName, ReadOnlyMemory<byte> preBuffer, ReadOnlyMemory<byte> postBuffer, TransmitFileOptions flags, CancellationToken cancellationToken = default)
+        {
+            if (cancellationToken.IsCancellationRequested)
+            {
+                return ValueTask.FromCanceled(cancellationToken);
+            }
+
+            if (!IsConnectionOriented)
+            {
+                var soex = new SocketException((int)SocketError.NotConnected);
+                return ValueTask.FromException(soex);
+            }
+
+            int packetsCount = 0;
+
+            if (fileName is not null)
+            {
+                packetsCount++;
+            }
+
+            if (!preBuffer.IsEmpty)
+            {
+                packetsCount++;
+            }
+
+            if (!postBuffer.IsEmpty)
+            {
+                packetsCount++;
+            }
+
+            AwaitableSocketAsyncEventArgs saea =
+                Interlocked.Exchange(ref _singleBufferSendEventArgs, null) ??
+                new AwaitableSocketAsyncEventArgs(this, isReceiveForCaching: false);
+
+            SendPacketsElement[] sendPacketsElements = saea.SendPacketsElements?.Length == packetsCount
+                ? saea.SendPacketsElements
+                : new SendPacketsElement[packetsCount];
+
+            int index = 0;
+            if (!preBuffer.IsEmpty)
+            {
+                sendPacketsElements[index++] = new SendPacketsElement(preBuffer, endOfPacket: index == packetsCount);
+            }
+
+            if (fileName is not null)
+            {
+                sendPacketsElements[index++] = new SendPacketsElement(fileName, 0, 0, endOfPacket: index == packetsCount);
+            }
+
+            if (!postBuffer.IsEmpty)
+            {
+                sendPacketsElements[index++] = new SendPacketsElement(postBuffer, endOfPacket: index == packetsCount);
+            }
+
+            Debug.Assert(index == packetsCount);
+
+            saea.SendPacketsFlags = flags;
+            saea.SendPacketsElements = sendPacketsElements;
+            saea.WrapExceptionsForNetworkStream = false;
+            return saea.SendPacketsAsync(this, cancellationToken);
+        }
+
         private static void ValidateBufferArguments(byte[] buffer, int offset, int size)
         {
             if (buffer == null)
@@ -1005,6 +1094,26 @@ namespace System.Net.Sockets
                     ValueTask.FromException(CreateException(error));
             }
 
+            public ValueTask SendPacketsAsync(Socket socket, CancellationToken cancellationToken)
+            {
+                Debug.Assert(Volatile.Read(ref _continuation) == null, "Expected null continuation to indicate reserved for use");
+
+                // TODO: Support cancellation by passing cancellationToken down through SendPacketsAsync, etc.
+                if (socket.SendPacketsAsync(this))
+                {
+                    _cancellationToken = cancellationToken;
+                    return new ValueTask(this, _token);
+                }
+
+                SocketError error = SocketError;
+
+                Release();
+
+                return error == SocketError.Success ?
+                    default :
+                    ValueTask.FromException(CreateException(error));
+            }
+
             public ValueTask<int> SendToAsync(Socket socket, CancellationToken cancellationToken)
             {
                 Debug.Assert(Volatile.Read(ref _continuation) == null, "Expected null continuation to indicate reserved for use");
index a46a305..594a09f 100644 (file)
@@ -226,61 +226,5 @@ namespace System.Net.Sockets
                 Send(postBuffer);
             }
         }
-
-        private async Task SendFileInternalAsync(FileStream? fileStream, byte[]? preBuffer, byte[]? postBuffer)
-        {
-            SocketError errorCode = SocketError.Success;
-            using (fileStream)
-            {
-                // Send the preBuffer, if any
-                // This will throw on error
-                if (preBuffer != null && preBuffer.Length > 0)
-                {
-                    // Using "this." makes the extension method kick in
-                    await this.SendAsync(new ArraySegment<byte>(preBuffer), SocketFlags.None).ConfigureAwait(false);
-                }
-
-                // Send the file, if any
-                if (fileStream != null)
-                {
-                    var tcs = new TaskCompletionSource<SocketError>();
-                    errorCode = SocketPal.SendFileAsync(_handle, fileStream, (_, socketError) => tcs.SetResult(socketError));
-                    if (errorCode == SocketError.IOPending)
-                    {
-                        errorCode = await tcs.Task.ConfigureAwait(false);
-                    }
-                }
-            }
-
-            if (errorCode != SocketError.Success)
-            {
-                UpdateSendSocketErrorForDisposed(ref errorCode);
-                UpdateStatusAfterSocketErrorAndThrowException(errorCode);
-            }
-
-            // Send the postBuffer, if any
-            // This will throw on error
-            if (postBuffer != null && postBuffer.Length > 0)
-            {
-                // Using "this." makes the extension method kick in
-                await this.SendAsync(new ArraySegment<byte>(postBuffer), SocketFlags.None).ConfigureAwait(false);
-            }
-        }
-
-        private IAsyncResult BeginSendFileInternal(string? fileName, byte[]? preBuffer, byte[]? postBuffer, TransmitFileOptions flags, AsyncCallback? callback, object? state)
-        {
-            CheckTransmitFileOptions(flags);
-
-            // Open the file, if any
-            // Open it before we send the preBuffer so that any exception happens first
-            FileStream? fileStream = OpenFile(fileName);
-
-            return TaskToApm.Begin(SendFileInternalAsync(fileStream, preBuffer, postBuffer), callback, state);
-        }
-
-        private void EndSendFileInternal(IAsyncResult asyncResult)
-        {
-            TaskToApm.End(asyncResult);
-        }
     }
 }
index 417386e..6f4e2ed 100644 (file)
@@ -400,59 +400,6 @@ namespace System.Net.Sockets
             }
         }
 
-        private IAsyncResult BeginSendFileInternal(string? fileName, byte[]? preBuffer, byte[]? postBuffer, TransmitFileOptions flags, AsyncCallback? callback, object? state)
-        {
-            FileStream? fileStream = OpenFile(fileName);
-
-            TransmitFileAsyncResult asyncResult = new TransmitFileAsyncResult(this, state, callback);
-            asyncResult.StartPostingAsyncOp(false);
-
-            SocketError errorCode = SocketPal.SendFileAsync(_handle, fileStream, preBuffer, postBuffer, flags, asyncResult);
-
-            // Check for synchronous exception
-            if (!CheckErrorAndUpdateStatus(errorCode))
-            {
-                UpdateSendSocketErrorForDisposed(ref errorCode);
-                throw new SocketException((int)errorCode);
-            }
-
-            asyncResult.FinishPostingAsyncOp();
-
-            return asyncResult;
-        }
-
-        private void EndSendFileInternal(IAsyncResult asyncResult)
-        {
-            TransmitFileAsyncResult? castedAsyncResult = asyncResult as TransmitFileAsyncResult;
-            if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this)
-            {
-                throw new ArgumentException(SR.net_io_invalidasyncresult, nameof(asyncResult));
-            }
-
-            if (castedAsyncResult.EndCalled)
-            {
-                throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, "EndSendFile"));
-            }
-
-            castedAsyncResult.InternalWaitForCompletion();
-            castedAsyncResult.EndCalled = true;
-
-            // If the user passed the Disconnect and/or ReuseSocket flags, then TransmitFile disconnected the socket.
-            // Update our state to reflect this.
-            if (castedAsyncResult.DoDisconnect)
-            {
-                SetToDisconnected();
-                _remoteEndPoint = null;
-            }
-
-            SocketError errorCode = (SocketError)castedAsyncResult.ErrorCode;
-            if (errorCode != SocketError.Success)
-            {
-                UpdateSendSocketErrorForDisposed(ref errorCode);
-                UpdateStatusAfterSocketErrorAndThrowException(errorCode);
-            }
-        }
-
         internal ThreadPoolBoundHandle GetOrAllocateThreadPoolBoundHandle() =>
             _handle.GetThreadPoolBoundHandle() ??
             GetOrAllocateThreadPoolBoundHandleSlow();
index 796bd6e..b7cddab 100644 (file)
@@ -2216,7 +2216,7 @@ namespace System.Net.Sockets
 
             if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"::DoBeginSendFile() SRC:{LocalEndPoint} DST:{RemoteEndPoint} fileName:{fileName}");
 
-            return BeginSendFileInternal(fileName, preBuffer, postBuffer, flags, callback, state);
+            return TaskToApm.Begin(SendFileAsync(fileName, preBuffer, postBuffer, flags).AsTask(), callback, state);
         }
 
         public void EndSendFile(IAsyncResult asyncResult)
@@ -2228,7 +2228,7 @@ namespace System.Net.Sockets
                 throw new ArgumentNullException(nameof(asyncResult));
             }
 
-            EndSendFileInternal(asyncResult);
+            TaskToApm.End(asyncResult);
         }
 
         public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, SocketFlags socketFlags, EndPoint remoteEP, AsyncCallback? callback, object? state)
index 46c9ad4..5b44cf4 100644 (file)
@@ -1881,9 +1881,6 @@ namespace System.Net.Sockets
             return GetSocketErrorForErrorCode(err);
         }
 
-        public static SocketError SendFileAsync(SafeSocketHandle handle, FileStream fileStream, Action<long, SocketError> callback) =>
-            SendFileAsync(handle, fileStream, 0, fileStream.Length, callback);
-
         private static SocketError SendFileAsync(SafeSocketHandle handle, FileStream fileStream, long offset, long count, Action<long, SocketError> callback)
         {
             long bytesSent;
index 8d10920..6e59eda 100644 (file)
@@ -1074,30 +1074,6 @@ namespace System.Net.Sockets
             }
         }
 
-        public static unsafe SocketError SendFileAsync(SafeSocketHandle handle, FileStream? fileStream, byte[]? preBuffer, byte[]? postBuffer, TransmitFileOptions flags, TransmitFileAsyncResult asyncResult)
-        {
-            asyncResult.SetUnmanagedStructures(fileStream, preBuffer, postBuffer, (flags & (TransmitFileOptions.Disconnect | TransmitFileOptions.ReuseSocket)) != 0);
-            try
-            {
-                bool success = TransmitFileHelper(
-                    handle,
-                    fileStream?.SafeFileHandle,
-                    asyncResult.DangerousOverlappedPointer, // SafeHandle was just created in SetUnmanagedStructures
-                    preBuffer is not null ? Marshal.UnsafeAddrOfPinnedArrayElement(preBuffer, 0) : IntPtr.Zero,
-                    preBuffer?.Length ?? 0,
-                    postBuffer is not null ? Marshal.UnsafeAddrOfPinnedArrayElement(postBuffer, 0) : IntPtr.Zero,
-                    postBuffer?.Length ?? 0,
-                    flags);
-
-                return asyncResult.ProcessOverlappedResult(success, 0);
-            }
-            catch
-            {
-                asyncResult.ReleaseUnmanagedStructures();
-                throw;
-            }
-        }
-
         public static void CheckDualModeReceiveSupport(Socket socket)
         {
             // Dual-mode sockets support received packet info on Windows.
diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TransmitFileAsyncResult.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TransmitFileAsyncResult.Windows.cs
deleted file mode 100644 (file)
index b78ad06..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.IO;
-
-namespace System.Net.Sockets
-{
-    internal sealed class TransmitFileAsyncResult : BaseOverlappedAsyncResult
-    {
-        private FileStream? _fileStream;
-        private bool _doDisconnect;
-
-        internal TransmitFileAsyncResult(Socket socket, object? asyncState, AsyncCallback? asyncCallback) :
-            base(socket, asyncState, asyncCallback)
-        {
-        }
-
-        internal void SetUnmanagedStructures(FileStream? fileStream, byte[]? preBuffer, byte[]? postBuffer, bool doDisconnect)
-        {
-            _fileStream = fileStream;
-            _doDisconnect = doDisconnect;
-
-            int buffsNumber = 0;
-
-            if (preBuffer != null && preBuffer.Length > 0)
-                ++buffsNumber;
-
-            if (postBuffer != null && postBuffer.Length > 0)
-                ++buffsNumber;
-
-            object[]? objectsToPin = null;
-            if (buffsNumber != 0)
-            {
-                objectsToPin = new object[buffsNumber];
-
-                if (preBuffer != null && preBuffer.Length > 0)
-                {
-                    objectsToPin[--buffsNumber] = preBuffer;
-                }
-
-                if (postBuffer != null && postBuffer.Length > 0)
-                {
-                    objectsToPin[--buffsNumber] = postBuffer;
-                }
-            }
-
-            base.SetUnmanagedStructures(objectsToPin);
-        }
-
-        protected override void ForceReleaseUnmanagedStructures()
-        {
-            if (_fileStream != null)
-            {
-                _fileStream.Dispose();
-                _fileStream = null;
-            }
-
-            base.ForceReleaseUnmanagedStructures();
-        }
-
-        internal bool DoDisconnect => _doDisconnect;
-    }
-}
index c22b0bc..319403d 100644 (file)
@@ -4,7 +4,6 @@
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
-using System.Threading;
 using System.Threading.Tasks;
 
 using Xunit;
@@ -28,12 +27,11 @@ namespace System.Net.Sockets.Tests
             await Assert.ThrowsAsync<ObjectDisposedException>(() => SendFileAsync(s, null, null, null, TransmitFileOptions.UseDefaultWorkerThread));
         }
 
-
         [Fact]
         public async Task NotConnected_ThrowsNotSupportedException()
         {
             using Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
-            
+
             await Assert.ThrowsAsync<NotSupportedException>(() => SendFileAsync(s, null));
             await Assert.ThrowsAsync<NotSupportedException>(() => SendFileAsync(s, null, null, null, TransmitFileOptions.UseDefaultWorkerThread));
         }
@@ -308,7 +306,7 @@ namespace System.Net.Sockets.Tests
                     await disposeTask;
 
                     SocketError? localSocketError = null;
-                    bool thrownDisposed = false;
+                    bool disposedException = false;
                     try
                     {
                         await socketOperation;
@@ -319,18 +317,22 @@ namespace System.Net.Sockets.Tests
                     }
                     catch (ObjectDisposedException)
                     {
-                        thrownDisposed = true;
+                        disposedException = true;
                     }
 
-                    if (UsesSync)
+                    if (UsesApm)
+                    {
+                        Assert.Null(localSocketError);
+                        Assert.True(disposedException);
+                    }
+                    else if (UsesSync)
                     {
                         Assert.Equal(SocketError.ConnectionAborted, localSocketError);
                     }
                     else
                     {
-                        Assert.True(thrownDisposed);
+                        Assert.Equal(SocketError.OperationAborted, localSocketError);
                     }
-                    
 
                     // On OSX, we're unable to unblock the on-going socket operations and
                     // perform an abortive close.
@@ -415,6 +417,11 @@ namespace System.Net.Sockets.Tests
         public SendFile_SyncForceNonBlocking(ITestOutputHelper output) : base(output) { }
     }
 
+    public sealed class SendFile_Task : SendFile<SocketHelperTask>
+    {
+        public SendFile_Task(ITestOutputHelper output) : base(output) { }
+    }
+
     public sealed class SendFile_Apm : SendFile<SocketHelperApm>
     {
         public SendFile_Apm(ITestOutputHelper output) : base(output) { }
index 983f7a2..1c27291 100644 (file)
@@ -246,8 +246,10 @@ namespace System.Net.Sockets.Tests
             s.SendAsync(bufferList, SocketFlags.None);
         public override Task<int> SendToAsync(Socket s, ArraySegment<byte> buffer, EndPoint endPoint) =>
             s.SendToAsync(buffer, SocketFlags.None, endPoint);
-        public override Task SendFileAsync(Socket s, string fileName) => throw new NotSupportedException();
-        public override Task SendFileAsync(Socket s, string fileName, ArraySegment<byte> preBuffer, ArraySegment<byte> postBuffer, TransmitFileOptions flags) => throw new NotSupportedException();
+        public override Task SendFileAsync(Socket s, string fileName) =>
+            s.SendFileAsync(fileName).AsTask();
+        public override Task SendFileAsync(Socket s, string fileName, ArraySegment<byte> preBuffer, ArraySegment<byte> postBuffer, TransmitFileOptions flags) =>
+            s.SendFileAsync(fileName, preBuffer, postBuffer, flags).AsTask();
         public override Task DisconnectAsync(Socket s, bool reuseSocket) =>
             s.DisconnectAsync(reuseSocket).AsTask();
     }
@@ -284,8 +286,10 @@ namespace System.Net.Sockets.Tests
             s.SendAsync(bufferList, SocketFlags.None);
         public override Task<int> SendToAsync(Socket s, ArraySegment<byte> buffer, EndPoint endPoint) =>
             s.SendToAsync(buffer, SocketFlags.None, endPoint, _cts.Token).AsTask() ;
-        public override Task SendFileAsync(Socket s, string fileName) => throw new NotSupportedException();
-        public override Task SendFileAsync(Socket s, string fileName, ArraySegment<byte> preBuffer, ArraySegment<byte> postBuffer, TransmitFileOptions flags) => throw new NotSupportedException();
+        public override Task SendFileAsync(Socket s, string fileName) =>
+            s.SendFileAsync(fileName, _cts.Token).AsTask();
+        public override Task SendFileAsync(Socket s, string fileName, ArraySegment<byte> preBuffer, ArraySegment<byte> postBuffer, TransmitFileOptions flags) =>
+            s.SendFileAsync(fileName, preBuffer, postBuffer, flags, _cts.Token).AsTask();
         public override Task DisconnectAsync(Socket s, bool reuseSocket) =>
             s.DisconnectAsync(reuseSocket, _cts.Token).AsTask();
     }