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; }
<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" />
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) :
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)
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");
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);
- }
}
}
}
}
- 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();
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)
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)
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;
}
}
- 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.
+++ /dev/null
-// 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;
- }
-}
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
using Xunit;
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));
}
await disposeTask;
SocketError? localSocketError = null;
- bool thrownDisposed = false;
+ bool disposedException = false;
try
{
await socketOperation;
}
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.
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) { }
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();
}
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();
}