UdpClient with span support (#53429)
authorLateApexEarlySpeed <72254037+lateapexearlyspeed@users.noreply.github.com>
Thu, 24 Jun 2021 12:30:55 +0000 (20:30 +0800)
committerGitHub <noreply@github.com>
Thu, 24 Jun 2021 12:30:55 +0000 (14:30 +0200)
Add API from #864

src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs
src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs
src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs
src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs
src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs
src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs

index 0ea7f6b..8ed4a71 100644 (file)
@@ -760,12 +760,19 @@ namespace System.Net.Sockets
         public void JoinMulticastGroup(System.Net.IPAddress multicastAddr, System.Net.IPAddress localAddress) { }
         public byte[] Receive([System.Diagnostics.CodeAnalysis.NotNullAttribute] ref System.Net.IPEndPoint? remoteEP) { throw null; }
         public System.Threading.Tasks.Task<System.Net.Sockets.UdpReceiveResult> ReceiveAsync() { throw null; }
+        public System.Threading.Tasks.ValueTask<System.Net.Sockets.UdpReceiveResult> ReceiveAsync(System.Threading.CancellationToken cancellationToken) { throw null; }
         public int Send(byte[] dgram, int bytes) { throw null; }
+        public int Send(System.ReadOnlySpan<byte> datagram) {throw null; }
         public int Send(byte[] dgram, int bytes, System.Net.IPEndPoint? endPoint) { throw null; }
+        public int Send(System.ReadOnlySpan<byte> datagram, System.Net.IPEndPoint? endPoint) { throw null; }
         public int Send(byte[] dgram, int bytes, string? hostname, int port) { throw null; }
+        public int Send(System.ReadOnlySpan<byte> datagram, string? hostname, int port) { throw null; }
         public System.Threading.Tasks.Task<int> SendAsync(byte[] datagram, int bytes) { throw null; }
+        public System.Threading.Tasks.ValueTask<int> SendAsync(System.ReadOnlyMemory<byte> datagram, System.Threading.CancellationToken cancellationToken = default)  { throw null; }
         public System.Threading.Tasks.Task<int> SendAsync(byte[] datagram, int bytes, System.Net.IPEndPoint? endPoint) { throw null; }
+        public System.Threading.Tasks.ValueTask<int> SendAsync(System.ReadOnlyMemory<byte> datagram, System.Net.IPEndPoint? endPoint, System.Threading.CancellationToken cancellationToken = default) { throw null; }
         public System.Threading.Tasks.Task<int> SendAsync(byte[] datagram, int bytes, string? hostname, int port) { throw null; }
+        public System.Threading.Tasks.ValueTask<int> SendAsync(System.ReadOnlyMemory<byte> datagram, string? hostname, int port, System.Threading.CancellationToken cancellationToken = default)  { throw null; }
     }
     public partial struct UdpReceiveResult : System.IEquatable<System.Net.Sockets.UdpReceiveResult>
     {
index e27a77d..246483d 100644 (file)
@@ -4,6 +4,7 @@
 using System.Diagnostics.CodeAnalysis;
 using System.Threading.Tasks;
 using System.Runtime.Versioning;
+using System.Threading;
 
 namespace System.Net.Sockets
 {
@@ -600,9 +601,46 @@ namespace System.Net.Sockets
         public Task<int> SendAsync(byte[] datagram, int bytes) =>
             SendAsync(datagram, bytes, null);
 
+        /// <summary>
+        /// Sends a UDP datagram asynchronously to a remote host.
+        /// </summary>
+        /// <param name="datagram">
+        /// An <see cref="ReadOnlyMemory{T}"/> of Type <see cref="byte"/> that specifies the UDP datagram that you intend to send.
+        /// </param>
+        /// <param name="cancellationToken">
+        /// The token to monitor for cancellation requests. The default value is None.
+        /// </param>
+        /// <returns>A <see cref="ValueTask{T}"/> that represents the asynchronous send operation. The value of its Result property contains the number of bytes sent.</returns>
+        /// <exception cref="ObjectDisposedException">The <see cref="UdpClient"/> is closed.</exception>
+        /// <exception cref="SocketException">An error occurred when accessing the socket.</exception>
+        public ValueTask<int> SendAsync(ReadOnlyMemory<byte> datagram, CancellationToken cancellationToken = default) =>
+            SendAsync(datagram, null, cancellationToken);
+
         public Task<int> SendAsync(byte[] datagram, int bytes, string? hostname, int port) =>
             SendAsync(datagram, bytes, GetEndpoint(hostname, port));
 
+        /// <summary>
+        /// Sends a UDP datagram asynchronously to a remote host.
+        /// </summary>
+        /// <param name="datagram">
+        /// An <see cref="ReadOnlyMemory{T}"/> of Type <see cref="byte"/> that specifies the UDP datagram that you intend to send.
+        /// </param>
+        /// <param name="hostname">
+        /// The name of the remote host to which you intend to send the datagram.
+        /// </param>
+        /// <param name="port">
+        /// The remote port number with which you intend to communicate.
+        /// </param>
+        /// <param name="cancellationToken">
+        /// The token to monitor for cancellation requests. The default value is None.
+        /// </param>
+        /// <returns>A <see cref="ValueTask{T}"/> that represents the asynchronous send operation. The value of its Result property contains the number of bytes sent.</returns>
+        /// <exception cref="InvalidOperationException">The <see cref="UdpClient"/> has already established a default remote host.</exception>
+        /// <exception cref="ObjectDisposedException">The <see cref="UdpClient"/> is closed.</exception>
+        /// <exception cref="SocketException">An error occurred when accessing the socket.</exception>
+        public ValueTask<int> SendAsync(ReadOnlyMemory<byte> datagram, string? hostname, int port, CancellationToken cancellationToken = default) =>
+            SendAsync(datagram, GetEndpoint(hostname, port), cancellationToken);
+
         public Task<int> SendAsync(byte[] datagram, int bytes, IPEndPoint? endPoint)
         {
             ValidateDatagram(datagram, bytes, endPoint);
@@ -618,6 +656,39 @@ namespace System.Net.Sockets
             }
         }
 
+        /// <summary>
+        /// Sends a UDP datagram asynchronously to a remote host.
+        /// </summary>
+        /// <param name="datagram">
+        /// An <see cref="ReadOnlyMemory{T}"/> of Type <see cref="byte"/> that specifies the UDP datagram that you intend to send.
+        /// </param>
+        /// <param name="endPoint">
+        /// An <see cref="IPEndPoint"/> that represents the host and port to which to send the datagram.
+        /// </param>
+        /// <param name="cancellationToken">
+        /// The token to monitor for cancellation requests. The default value is None.
+        /// </param>
+        /// <returns>A <see cref="ValueTask{T}"/> that represents the asynchronous send operation. The value of its Result property contains the number of bytes sent.</returns>
+        /// <exception cref="InvalidOperationException"><see cref="UdpClient"/> has already established a default remote host and <paramref name="endPoint"/> is not <see langword="null"/>.</exception>
+        /// <exception cref="ObjectDisposedException">The <see cref="UdpClient"/> is closed.</exception>
+        /// <exception cref="SocketException">An error occurred when accessing the socket.</exception>
+        public ValueTask<int> SendAsync(ReadOnlyMemory<byte> datagram, IPEndPoint? endPoint, CancellationToken cancellationToken = default)
+        {
+            ThrowIfDisposed();
+
+            if (endPoint is null)
+            {
+                return _clientSocket.SendAsync(datagram, SocketFlags.None, cancellationToken);
+            }
+            if (_active)
+            {
+                // Do not allow sending packets to arbitrary host when connected.
+                throw new InvalidOperationException(SR.net_udpconnected);
+            }
+            CheckForBroadcast(endPoint.Address);
+            return _clientSocket.SendToAsync(datagram, SocketFlags.None, endPoint, cancellationToken);
+        }
+
         public Task<UdpReceiveResult> ReceiveAsync()
         {
             ThrowIfDisposed();
@@ -639,6 +710,36 @@ namespace System.Net.Sockets
             }
         }
 
+        /// <summary>
+        /// Returns a UDP datagram asynchronously that was sent by a remote host.
+        /// </summary>
+        /// <param name="cancellationToken">
+        /// The token to monitor for cancellation requests.
+        /// </param>
+        /// <returns>A <see cref="ValueTask{TResult}"/> representing the asynchronous operation.</returns>
+        /// <exception cref="ObjectDisposedException">The underlying <see cref="Socket"/> has been closed.</exception>
+        /// <exception cref="SocketException">An error occurred when accessing the socket.</exception>
+        public ValueTask<UdpReceiveResult> ReceiveAsync(CancellationToken cancellationToken)
+        {
+            ThrowIfDisposed();
+
+            return WaitAndWrap(_clientSocket.ReceiveFromAsync(
+                _buffer,
+                SocketFlags.None,
+                _family == AddressFamily.InterNetwork ? IPEndPointStatics.Any : IPEndPointStatics.IPv6Any, cancellationToken));
+
+            async ValueTask<UdpReceiveResult> WaitAndWrap(ValueTask<SocketReceiveFromResult> task)
+            {
+                SocketReceiveFromResult result = await task.ConfigureAwait(false);
+
+                byte[] buffer = result.ReceivedBytes < MaxUDPSize ?
+                    _buffer.AsSpan(0, result.ReceivedBytes).ToArray() :
+                    _buffer;
+
+                return new UdpReceiveResult(buffer, (IPEndPoint)result.RemoteEndPoint);
+            }
+        }
+
         private void CreateClientSocket()
         {
             // Common initialization code.
@@ -892,45 +993,59 @@ namespace System.Net.Sockets
             return Client.SendTo(dgram, 0, bytes, SocketFlags.None, endPoint);
         }
 
-
-        // Sends a UDP datagram to the specified port on the specified remote host.
-        public int Send(byte[] dgram, int bytes, string? hostname, int port)
+        /// <summary>
+        /// Sends a UDP datagram to the host at the specified remote endpoint.
+        /// </summary>
+        /// <param name="datagram">
+        /// An <see cref="ReadOnlySpan{T}"/> of Type <see cref="byte"/> that specifies the UDP datagram that you intend to send.
+        /// </param>
+        /// <param name="endPoint">
+        /// An <see cref="IPEndPoint"/> that represents the host and port to which to send the datagram.
+        /// </param>
+        /// <returns>The number of bytes sent.</returns>
+        /// <exception cref="InvalidOperationException"><see cref="UdpClient"/> has already established a default remote host and <paramref name="endPoint"/> is not <see langword="null"/>.</exception>
+        /// <exception cref="ObjectDisposedException"><see cref="UdpClient"/> is closed.</exception>
+        /// <exception cref="SocketException">An error occurred when accessing the socket.</exception>
+        public int Send(ReadOnlySpan<byte> datagram, IPEndPoint? endPoint)
         {
             ThrowIfDisposed();
 
-            if (dgram == null)
-            {
-                throw new ArgumentNullException(nameof(dgram));
-            }
-            if (_active && ((hostname != null) || (port != 0)))
+            if (_active && endPoint != null)
             {
                 // Do not allow sending packets to arbitrary host when connected
                 throw new InvalidOperationException(SR.net_udpconnected);
             }
 
-            if (hostname == null || port == 0)
-            {
-                return Client.Send(dgram, 0, bytes, SocketFlags.None);
-            }
-
-            IPAddress[] addresses = Dns.GetHostAddresses(hostname);
-
-            int i = 0;
-            for (; i < addresses.Length && !IsAddressFamilyCompatible(addresses[i].AddressFamily); i++)
+            if (endPoint == null)
             {
-                ; // just count the addresses
+                return Client.Send(datagram, SocketFlags.None);
             }
 
-            if (addresses.Length == 0 || i == addresses.Length)
-            {
-                throw new ArgumentException(SR.net_invalidAddressList, nameof(hostname));
-            }
+            CheckForBroadcast(endPoint.Address);
 
-            CheckForBroadcast(addresses[i]);
-            IPEndPoint ipEndPoint = new IPEndPoint(addresses[i], port);
-            return Client.SendTo(dgram, 0, bytes, SocketFlags.None, ipEndPoint);
+            return Client.SendTo(datagram, SocketFlags.None, endPoint);
         }
 
+        // Sends a UDP datagram to the specified port on the specified remote host.
+        public int Send(byte[] dgram, int bytes, string? hostname, int port) => Send(dgram, bytes, GetEndpoint(hostname, port));
+
+        /// <summary>
+        /// Sends a UDP datagram to a specified port on a specified remote host.
+        /// </summary>
+        /// <param name="datagram">
+        /// An <see cref="ReadOnlySpan{T}"/> of Type <see cref="byte"/> that specifies the UDP datagram that you intend to send.
+        /// </param>
+        /// <param name="hostname">
+        /// The name of the remote host to which you intend to send the datagram.
+        /// </param>
+        /// <param name="port">
+        /// The remote port number with which you intend to communicate.
+        /// </param>
+        /// <returns>The number of bytes sent.</returns>
+        /// <exception cref="InvalidOperationException">The <see cref="UdpClient"/> has already established a default remote host.</exception>
+        /// <exception cref="ObjectDisposedException">The <see cref="UdpClient"/> is closed.</exception>
+        /// <exception cref="SocketException">An error occurred when accessing the socket.</exception>
+        public int Send(ReadOnlySpan<byte> datagram, string? hostname, int port) => Send(datagram, GetEndpoint(hostname, port));
 
         // Sends a UDP datagram to a remote host.
         public int Send(byte[] dgram, int bytes)
@@ -950,6 +1065,29 @@ namespace System.Net.Sockets
             return Client.Send(dgram, 0, bytes, SocketFlags.None);
         }
 
+        /// <summary>
+        /// Sends a UDP datagram to a remote host.
+        /// </summary>
+        /// <param name="datagram">
+        /// An <see cref="ReadOnlySpan{T}"/> of Type <see cref="byte"/> that specifies the UDP datagram that you intend to send.
+        /// </param>
+        /// <returns>The number of bytes sent.</returns>
+        /// <exception cref="InvalidOperationException">The <see cref="UdpClient"/> has not established a default remote host.</exception>
+        /// <exception cref="ObjectDisposedException">The <see cref="UdpClient"/> is closed.</exception>
+        /// <exception cref="SocketException">An error occurred when accessing the socket.</exception>
+        public int Send(ReadOnlySpan<byte> datagram)
+        {
+            ThrowIfDisposed();
+
+            if (!_active)
+            {
+                // only allowed on connected socket
+                throw new InvalidOperationException(SR.net_notconnected);
+            }
+
+            return Client.Send(datagram, SocketFlags.None);
+        }
+
         private void ThrowIfDisposed()
         {
             if (_disposed)
index 25af4a4..55535f0 100644 (file)
@@ -6,7 +6,7 @@ using System.Diagnostics.CodeAnalysis;
 namespace System.Net.Sockets
 {
     /// <summary>
-    /// Presents UDP receive result information from a call to the <see cref="UdpClient.ReceiveAsync"/> method
+    /// Presents UDP receive result information from a call to the <see cref="UdpClient.ReceiveAsync()"/> and <see cref="UdpClient.ReceiveAsync(System.Threading.CancellationToken)"/> method
     /// </summary>
     public struct UdpReceiveResult : IEquatable<UdpReceiveResult>
     {
index ce83af9..252b862 100644 (file)
@@ -11,8 +11,8 @@ namespace System.Net.Sockets.Tests
     {
         [OuterLoop]
         [Theory]
-        [MemberData(nameof(Loopbacks))]
-        public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackAddress)
+        [MemberData(nameof(LoopbacksAndUseMemory))]
+        public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackAddress, bool useMemoryOverload)
         {
             IPAddress leftAddress = loopbackAddress, rightAddress = loopbackAddress;
 
@@ -66,7 +66,7 @@ namespace System.Net.Sockets.Tests
                         random.NextBytes(sendBuffer);
                         sendBuffer[0] = (byte)sentDatagrams;
 
-                        int sent = await right.SendAsync(sendBuffer, DatagramSize, leftEndpoint);
+                        int sent = useMemoryOverload ? await right.SendAsync(new ReadOnlyMemory<byte>(sendBuffer), leftEndpoint) : await right.SendAsync(sendBuffer, DatagramSize, leftEndpoint);
 
                         Assert.True(receiverAck.Wait(AckTimeout));
                         receiverAck.Reset();
@@ -85,5 +85,13 @@ namespace System.Net.Sockets.Tests
                 }
             }
         }
+
+        public static readonly object[][] LoopbacksAndUseMemory = new object[][]
+        {
+            new object[] { IPAddress.IPv6Loopback, true },
+            new object[] { IPAddress.IPv6Loopback, false },
+            new object[] { IPAddress.Loopback, true },
+            new object[] { IPAddress.Loopback, false },
+        };
     }
 }
index 82bed83..7b507a1 100644 (file)
@@ -325,8 +325,8 @@ namespace System.Net.Sockets.Tests
                         await new SendReceive_Apm(null).SendRecv_Stream_TCP(IPAddress.Loopback, false).ConfigureAwait(false);
                         await new SendReceive_Apm(null).SendRecv_Stream_TCP(IPAddress.Loopback, true).ConfigureAwait(false);
 
-                        await new SendReceiveUdpClient().SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress.Loopback).ConfigureAwait(false);
-                        await new SendReceiveUdpClient().SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress.Loopback).ConfigureAwait(false);
+                        await new SendReceiveUdpClient().SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress.Loopback, false).ConfigureAwait(false);
+                        await new SendReceiveUdpClient().SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress.Loopback, false).ConfigureAwait(false);
 
                         await new NetworkStreamTest().CopyToAsync_AllDataCopied(4096, true).ConfigureAwait(false);
                         await new NetworkStreamTest().Timeout_Roundtrips().ConfigureAwait(false);
index 3b44796..66f69e1 100644 (file)
@@ -59,7 +59,6 @@ namespace System.Net.Sockets.Tests
             AssertExtensions.Throws<ArgumentNullException>("localEP", () => new UdpClient(null));
         }
 
-        [OuterLoop]
         [Fact]
         public void Ctor_CanSend()
         {
@@ -70,7 +69,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [OuterLoop]
         [Fact]
         public void Ctor_Int_CanSend()
         {
@@ -88,7 +86,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [OuterLoop]
         [Fact]
         public void Ctor_IntAddressFamily_IPv4_CanSend()
         {
@@ -106,7 +103,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [OuterLoop]
         [Fact]
         public void Ctor_IntAddressFamily_IPv6_CanSend()
         {
@@ -124,7 +120,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [OuterLoop]
         [Fact]
         public void Ctor_IPEndPoint_CanSend()
         {
@@ -142,7 +137,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [OuterLoop]
         [Fact]
         public void Ctor_StringInt_CanSend()
         {
@@ -191,6 +185,21 @@ namespace System.Net.Sockets.Tests
             Assert.Throws<ObjectDisposedException>(() => udpClient.Send(null, 0, remoteEP));
             Assert.Throws<ObjectDisposedException>(() => udpClient.Send(null, 0));
             Assert.Throws<ObjectDisposedException>(() => udpClient.Send(null, 0, "localhost", 0));
+
+            Assert.Throws<ObjectDisposedException>(() => udpClient.Send(new ReadOnlySpan<byte>(), remoteEP));
+            Assert.Throws<ObjectDisposedException>(() => udpClient.Send(new ReadOnlySpan<byte>()));
+            Assert.Throws<ObjectDisposedException>(() => udpClient.Send(new ReadOnlySpan<byte>(), "localhost", 0));
+
+            Assert.Throws<ObjectDisposedException>(() => {udpClient.SendAsync(null, 0, remoteEP);});
+            Assert.Throws<ObjectDisposedException>(() => {udpClient.SendAsync(null, 0);});
+            Assert.Throws<ObjectDisposedException>(() => {udpClient.SendAsync(null, 0, "localhost", 0);});
+                       
+            Assert.Throws<ObjectDisposedException>(() => udpClient.SendAsync(new ReadOnlyMemory<byte>(), remoteEP));
+            Assert.Throws<ObjectDisposedException>(() => udpClient.SendAsync(new ReadOnlyMemory<byte>()));
+            Assert.Throws<ObjectDisposedException>(() => udpClient.SendAsync(new ReadOnlyMemory<byte>(), "localhost", 0));
+
+            Assert.Throws<ObjectDisposedException>(() => {udpClient.ReceiveAsync();});
+            Assert.Throws<ObjectDisposedException>(() => udpClient.ReceiveAsync(default));
         }
 
         [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))]
@@ -321,7 +330,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [OuterLoop]
         [Fact]
         public void BeginSend_NegativeBytes_Throws()
         {
@@ -337,7 +345,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [OuterLoop]
         [Fact]
         public void BeginSend_BytesMoreThanArrayLength_Throws()
         {
@@ -353,7 +360,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [OuterLoop]
         [Fact]
         public void BeginSend_AsyncOperationCompletes_Success()
         {
@@ -377,8 +383,12 @@ namespace System.Net.Sockets.Tests
                 AssertExtensions.Throws<ArgumentNullException>("dgram", () => udpClient.Send(null, 0, "localhost", 0));
                 AssertExtensions.Throws<ArgumentNullException>("dgram", () => udpClient.Send(null, 0, new IPEndPoint(IPAddress.Loopback, 0)));
                 Assert.Throws<InvalidOperationException>(() => udpClient.Send(new byte[1], 1));
+                Assert.Throws<InvalidOperationException>(() => udpClient.Send(new ReadOnlySpan<byte>(new byte[1])));
                 udpClient.Active = true;
                 Assert.Throws<InvalidOperationException>(() => udpClient.Send(new byte[1], 1, new IPEndPoint(IPAddress.Loopback, 0)));
+                Assert.Throws<InvalidOperationException>(() => udpClient.Send(new ReadOnlySpan<byte>(new byte[1]), new IPEndPoint(IPAddress.Loopback, 0)));
+                Assert.Throws<InvalidOperationException>(() => {udpClient.SendAsync(new byte[1], 1, new IPEndPoint(IPAddress.Loopback, 0));});
+                Assert.Throws<InvalidOperationException>(() => udpClient.SendAsync(new ReadOnlyMemory<byte>(new byte[1]), new IPEndPoint(IPAddress.Loopback, 0)));
             }
         }
 
@@ -389,10 +399,17 @@ namespace System.Net.Sockets.Tests
             using (var udpClient = new UdpClient("localhost", 0))
             {
                 Assert.Throws<InvalidOperationException>(() => udpClient.Send(new byte[1], 1, "localhost", 0));
+                Assert.Throws<InvalidOperationException>(() => udpClient.Send(new ReadOnlySpan<byte>(new byte[1]), "localhost", 0));
+                Assert.Throws<InvalidOperationException>(() => {udpClient.SendAsync(new byte[1], 1, "localhost", 0);});
+                Assert.Throws<InvalidOperationException>(() => udpClient.SendAsync(new ReadOnlyMemory<byte>(new byte[1]), "localhost", 0));
+                
+                Assert.Throws<InvalidOperationException>(() => udpClient.Send(new byte[1], 1, null, UnusedPort));
+                Assert.Throws<InvalidOperationException>(() => udpClient.Send(new ReadOnlySpan<byte>(new byte[1]), null, UnusedPort));
+                Assert.Throws<InvalidOperationException>(() => {udpClient.SendAsync(new byte[1], 1, null, UnusedPort);});
+                Assert.Throws<InvalidOperationException>(() => udpClient.SendAsync(new ReadOnlyMemory<byte>(new byte[1]), null, UnusedPort));
             }
         }
 
-        [OuterLoop]
         [Fact]
         public void Client_Idempotent()
         {
@@ -421,7 +438,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [OuterLoop]
         [Fact]
         public async Task ConnectAsync_StringHost_Success()
         {
@@ -431,7 +447,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [OuterLoop]
         [Fact]
         public async Task ConnectAsync_IPAddressHost_Success()
         {
@@ -441,7 +456,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [OuterLoop]
         [Fact]
         public void Connect_StringHost_Success()
         {
@@ -451,7 +465,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [OuterLoop]
         [Fact]
         public void Connect_IPAddressHost_Success()
         {
@@ -468,7 +481,6 @@ namespace System.Net.Sockets.Tests
             _waitHandle.Set();
         }
 
-        [OuterLoop]
         [Theory]
         [PlatformSpecific(TestPlatforms.Windows)]  // Udp.AllowNatTraversal only supported on Windows
         [InlineData(true, IPProtectionLevel.Unrestricted)]
@@ -482,7 +494,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [OuterLoop]
         [Theory]
         [PlatformSpecific(TestPlatforms.AnyUnix)]  // Udp.AllowNatTraversal throws PNSE on Unix
         [InlineData(true)]
@@ -495,7 +506,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [OuterLoop]
         [Theory]
         [InlineData(false)]
         [InlineData(true)]
@@ -507,32 +517,55 @@ namespace System.Net.Sockets.Tests
             using (var sender = new UdpClient(new IPEndPoint(address, 0)))
             {
                 sender.Send(new byte[1], 1, new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port));
+                AssertReceive(receiver);
 
-                IPEndPoint remoteEP = null;
-                byte[] data = receiver.Receive(ref remoteEP);
-                Assert.NotNull(remoteEP);
-                Assert.InRange(data.Length, 1, int.MaxValue);
+                sender.Send(new ReadOnlySpan<byte>(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port));
+                AssertReceive(receiver);
+            }
+        }
+
+        [Theory]
+        [InlineData(false)]
+        [InlineData(true)]
+        [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix
+        public void Send_Receive_With_HostName_Success(bool ipv4)
+        {
+            IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback;
+
+            using (var receiver = new UdpClient(new IPEndPoint(address, 0)))
+            using (var sender = new UdpClient(new IPEndPoint(address, 0)))
+            {
+                sender.Send(new byte[1], 1, "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port);
+                AssertReceive(receiver);
+
+                sender.Send(new ReadOnlySpan<byte>(new byte[1]), "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port);
+                AssertReceive(receiver);
             }
         }
 
         [Fact]
         [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix
-        [OuterLoop]
         public void Send_Receive_Connected_Success()
         {
             using (var receiver = new UdpClient("localhost", 0))
             using (var sender = new UdpClient("localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port))
             {
                 sender.Send(new byte[1], 1);
+                AssertReceive(receiver);
 
-                IPEndPoint remoteEP = null;
-                byte[] data = receiver.Receive(ref remoteEP);
-                Assert.NotNull(remoteEP);
-                Assert.InRange(data.Length, 1, int.MaxValue);
+                sender.Send(new ReadOnlySpan<byte>(new byte[1]));
+                AssertReceive(receiver);
             }
         }
 
-        [OuterLoop]
+        private static void AssertReceive(UdpClient receiver)
+        {
+            IPEndPoint remoteEP = null;
+            byte[] data = receiver.Receive(ref remoteEP);
+            Assert.NotNull(remoteEP);
+            Assert.InRange(data.Length, 1, int.MaxValue);
+        }
+
         [Theory]
         [InlineData(false)]
         [InlineData(true)]
@@ -549,7 +582,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [OuterLoop]
         [Theory]
         [InlineData(false)]
         [InlineData(true)]
@@ -571,7 +603,6 @@ namespace System.Net.Sockets.Tests
 
         [Fact]
         [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix
-        [OuterLoop]
         public void BeginEndSend_BeginEndReceive_Connected_Success()
         {
             using (var receiver = new UdpClient("localhost", 0))
@@ -586,7 +617,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [OuterLoop]
         [Theory]
         [InlineData(false)]
         [InlineData(true)]
@@ -598,28 +628,114 @@ namespace System.Net.Sockets.Tests
             using (var sender = new UdpClient(new IPEndPoint(address, 0)))
             {
                 await sender.SendAsync(new byte[1], 1, new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port));
+                               await AssertReceiveAsync(receiver);
+                               
+                               await sender.SendAsync(new ReadOnlyMemory<byte>(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port));
+                               await AssertReceiveAsync(receiver);
+            }
+        }
 
-                UdpReceiveResult result = await receiver.ReceiveAsync();
-                Assert.NotNull(result.RemoteEndPoint);
-                Assert.NotNull(result.Buffer);
-                Assert.InRange(result.Buffer.Length, 1, int.MaxValue);
+        [Theory]
+        [InlineData(false)]
+        [InlineData(true)]
+        [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix
+        public async Task SendAsync_ReceiveAsync_With_HostName_Success(bool ipv4)
+        {
+            IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback;
+
+            using (var receiver = new UdpClient(new IPEndPoint(address, 0)))
+            using (var sender = new UdpClient(new IPEndPoint(address, 0)))
+            {
+                await sender.SendAsync(new byte[1], "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port);
+                await AssertReceiveAsync(receiver);
+
+                await sender.SendAsync(new ReadOnlyMemory<byte>(new byte[1]), "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port);
+                await AssertReceiveAsync(receiver);
+            }
+        }
+
+        [Theory]
+        [InlineData(false)]
+        [InlineData(true)]
+        public async Task ReceiveAsync_Cancel_Throw(bool ipv4)
+        {
+            IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback;
+            
+            using (var receiver = new UdpClient(new IPEndPoint(address, 0)))
+            {
+                using (var timeoutCts = new CancellationTokenSource(1))
+                {
+                    await Assert.ThrowsAnyAsync<OperationCanceledException>(() => receiver.ReceiveAsync(timeoutCts.Token).AsTask());
+                }
             }
         }
 
         [Fact]
         [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix
-        [OuterLoop]
         public async Task SendAsync_ReceiveAsync_Connected_Success()
         {
             using (var receiver = new UdpClient("localhost", 0))
             using (var sender = new UdpClient("localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port))
             {
                 await sender.SendAsync(new byte[1], 1);
+                await AssertReceiveAsync(receiver);
+
+                await sender.SendAsync(new ReadOnlyMemory<byte>(new byte[1]));
+                await AssertReceiveAsync(receiver);
+
+                await sender.SendAsync(new ReadOnlyMemory<byte>(new byte[1]), null);
+                await AssertReceiveAsync(receiver);
+
+                await sender.SendAsync(new ReadOnlyMemory<byte>(new byte[1]), null, 0);
+                await AssertReceiveAsync(receiver);
+            }
+        }
+
+        private static async Task AssertReceiveAsync(UdpClient receiver)
+        {
+            UdpReceiveResult result = await receiver.ReceiveAsync();
+            Assert.NotNull(result.RemoteEndPoint);
+            Assert.NotNull(result.Buffer);
+            Assert.InRange(result.Buffer.Length, 1, int.MaxValue);
+        }
+
+        [Fact]
+        [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix
+        public async Task SendAsync_Connected_PreCanceled_Throws()
+        {
+            using (var receiver = new UdpClient("localhost", 0))
+            using (var sender = new UdpClient("localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port))
+            {
+                await Assert.ThrowsAnyAsync<OperationCanceledException>(() => sender.SendAsync(new ReadOnlyMemory<byte>(new byte[1]), new CancellationToken(true)).AsTask());
+            }
+        }
 
-                UdpReceiveResult result = await receiver.ReceiveAsync();
-                Assert.NotNull(result.RemoteEndPoint);
-                Assert.NotNull(result.Buffer);
-                Assert.InRange(result.Buffer.Length, 1, int.MaxValue);
+        [Theory]
+        [InlineData(false)]
+        [InlineData(true)]
+        [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix
+        public async Task SendAsync_With_HostName_PreCanceled_Throws(bool ipv4)
+        {
+            IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback;
+
+            using (var receiver = new UdpClient(new IPEndPoint(address, 0)))
+            using (var sender = new UdpClient(new IPEndPoint(address, 0)))
+            {
+                await Assert.ThrowsAnyAsync<OperationCanceledException>(() => sender.SendAsync(new ReadOnlyMemory<byte>(new byte[1]), "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port, new CancellationToken(true)).AsTask());
+            }
+        }
+
+        [Theory]
+        [InlineData(false)]
+        [InlineData(true)]
+        public async Task SendAsync_PreCanceled_Throws(bool ipv4)
+        {
+            IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback;
+
+            using (var receiver = new UdpClient(new IPEndPoint(address, 0)))
+            using (var sender = new UdpClient(new IPEndPoint(address, 0)))
+            {
+                await Assert.ThrowsAnyAsync<OperationCanceledException>(() => sender.SendAsync(new ReadOnlyMemory<byte>(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port), new CancellationToken(true)).AsTask());
             }
         }