diff --git a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs index b128e5c232d8b..721a083b3ec18 100644 --- a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs +++ b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs @@ -756,12 +756,19 @@ public void JoinMulticastGroup(System.Net.IPAddress multicastAddr, int timeToLiv 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 ReceiveAsync() { throw null; } + public System.Threading.Tasks.ValueTask ReceiveAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public int Send(byte[] dgram, int bytes) { throw null; } + public int Send(System.ReadOnlySpan datagram) {throw null; } public int Send(byte[] dgram, int bytes, System.Net.IPEndPoint? endPoint) { throw null; } + public int Send(System.ReadOnlySpan 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 datagram, string? hostname, int port) { throw null; } public System.Threading.Tasks.Task SendAsync(byte[] datagram, int bytes) { throw null; } + public System.Threading.Tasks.ValueTask SendAsync(System.ReadOnlyMemory datagram, System.Threading.CancellationToken cancellationToken = default) { throw null; } public System.Threading.Tasks.Task SendAsync(byte[] datagram, int bytes, System.Net.IPEndPoint? endPoint) { throw null; } + public System.Threading.Tasks.ValueTask SendAsync(System.ReadOnlyMemory datagram, System.Net.IPEndPoint? endPoint, System.Threading.CancellationToken cancellationToken = default) { throw null; } public System.Threading.Tasks.Task SendAsync(byte[] datagram, int bytes, string? hostname, int port) { throw null; } + public System.Threading.Tasks.ValueTask SendAsync(System.ReadOnlyMemory datagram, string? hostname, int port, System.Threading.CancellationToken cancellationToken = default) { throw null; } } public partial struct UdpReceiveResult : System.IEquatable { diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs index e27a77da1f81d..246483d0815fb 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs @@ -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 @@ public void DropMulticastGroup(IPAddress multicastAddr, int ifindex) public Task SendAsync(byte[] datagram, int bytes) => SendAsync(datagram, bytes, null); + /// + /// Sends a UDP datagram asynchronously to a remote host. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// + /// The token to monitor for cancellation requests. The default value is None. + /// + /// A that represents the asynchronous send operation. The value of its Result property contains the number of bytes sent. + /// The is closed. + /// An error occurred when accessing the socket. + public ValueTask SendAsync(ReadOnlyMemory datagram, CancellationToken cancellationToken = default) => + SendAsync(datagram, null, cancellationToken); + public Task SendAsync(byte[] datagram, int bytes, string? hostname, int port) => SendAsync(datagram, bytes, GetEndpoint(hostname, port)); + /// + /// Sends a UDP datagram asynchronously to a remote host. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// + /// The name of the remote host to which you intend to send the datagram. + /// + /// + /// The remote port number with which you intend to communicate. + /// + /// + /// The token to monitor for cancellation requests. The default value is None. + /// + /// A that represents the asynchronous send operation. The value of its Result property contains the number of bytes sent. + /// The has already established a default remote host. + /// The is closed. + /// An error occurred when accessing the socket. + public ValueTask SendAsync(ReadOnlyMemory datagram, string? hostname, int port, CancellationToken cancellationToken = default) => + SendAsync(datagram, GetEndpoint(hostname, port), cancellationToken); + public Task SendAsync(byte[] datagram, int bytes, IPEndPoint? endPoint) { ValidateDatagram(datagram, bytes, endPoint); @@ -618,6 +656,39 @@ public Task SendAsync(byte[] datagram, int bytes, IPEndPoint? endPoint) } } + /// + /// Sends a UDP datagram asynchronously to a remote host. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// + /// An that represents the host and port to which to send the datagram. + /// + /// + /// The token to monitor for cancellation requests. The default value is None. + /// + /// A that represents the asynchronous send operation. The value of its Result property contains the number of bytes sent. + /// has already established a default remote host and is not . + /// The is closed. + /// An error occurred when accessing the socket. + public ValueTask SendAsync(ReadOnlyMemory 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 ReceiveAsync() { ThrowIfDisposed(); @@ -639,6 +710,36 @@ async Task WaitAndWrap(Task task) } } + /// + /// Returns a UDP datagram asynchronously that was sent by a remote host. + /// + /// + /// The token to monitor for cancellation requests. + /// + /// A representing the asynchronous operation. + /// The underlying has been closed. + /// An error occurred when accessing the socket. + public ValueTask ReceiveAsync(CancellationToken cancellationToken) + { + ThrowIfDisposed(); + + return WaitAndWrap(_clientSocket.ReceiveFromAsync( + _buffer, + SocketFlags.None, + _family == AddressFamily.InterNetwork ? IPEndPointStatics.Any : IPEndPointStatics.IPv6Any, cancellationToken)); + + async ValueTask WaitAndWrap(ValueTask 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 @@ public int Send(byte[] dgram, int bytes, IPEndPoint? endPoint) 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) + /// + /// Sends a UDP datagram to the host at the specified remote endpoint. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// + /// An that represents the host and port to which to send the datagram. + /// + /// The number of bytes sent. + /// has already established a default remote host and is not . + /// is closed. + /// An error occurred when accessing the socket. + public int Send(ReadOnlySpan 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)); + + /// + /// Sends a UDP datagram to a specified port on a specified remote host. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// + /// The name of the remote host to which you intend to send the datagram. + /// + /// + /// The remote port number with which you intend to communicate. + /// + /// The number of bytes sent. + /// The has already established a default remote host. + /// The is closed. + /// An error occurred when accessing the socket. + public int Send(ReadOnlySpan 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 @@ public int Send(byte[] dgram, int bytes) return Client.Send(dgram, 0, bytes, SocketFlags.None); } + /// + /// Sends a UDP datagram to a remote host. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// The number of bytes sent. + /// The has not established a default remote host. + /// The is closed. + /// An error occurred when accessing the socket. + public int Send(ReadOnlySpan 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) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs index 25af4a45bbaa1..55535f0583009 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs @@ -6,7 +6,7 @@ namespace System.Net.Sockets { /// - /// Presents UDP receive result information from a call to the method + /// Presents UDP receive result information from a call to the and method /// public struct UdpReceiveResult : IEquatable { diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs index ce83af9bb677d..252b862bce1ab 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs @@ -11,8 +11,8 @@ public sealed class SendReceiveUdpClient : MemberDatas { [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 @@ public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackA random.NextBytes(sendBuffer); sendBuffer[0] = (byte)sentDatagrams; - int sent = await right.SendAsync(sendBuffer, DatagramSize, leftEndpoint); + int sent = useMemoryOverload ? await right.SendAsync(new ReadOnlyMemory(sendBuffer), leftEndpoint) : await right.SendAsync(sendBuffer, DatagramSize, leftEndpoint); Assert.True(receiverAck.Wait(AckTimeout)); receiverAck.Reset(); @@ -85,5 +85,13 @@ public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackA } } } + + 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 }, + }; } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs index ad17579f90e6a..8287f8b0657d0 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs @@ -374,8 +374,8 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn 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); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs index 1137613a062c1..f4f967ca45ef9 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs @@ -59,7 +59,6 @@ public void Ctor_NullEndpoint_Throws() AssertExtensions.Throws("localEP", () => new UdpClient(null)); } - [OuterLoop] [Fact] public void Ctor_CanSend() { @@ -70,7 +69,6 @@ public void Ctor_CanSend() } } - [OuterLoop] [Fact] public void Ctor_Int_CanSend() { @@ -88,7 +86,6 @@ public void Ctor_Int_CanSend() } } - [OuterLoop] [Fact] public void Ctor_IntAddressFamily_IPv4_CanSend() { @@ -106,7 +103,6 @@ public void Ctor_IntAddressFamily_IPv4_CanSend() } } - [OuterLoop] [Fact] public void Ctor_IntAddressFamily_IPv6_CanSend() { @@ -124,7 +120,6 @@ public void Ctor_IntAddressFamily_IPv6_CanSend() } } - [OuterLoop] [Fact] public void Ctor_IPEndPoint_CanSend() { @@ -142,7 +137,6 @@ public void Ctor_IPEndPoint_CanSend() } } - [OuterLoop] [Fact] public void Ctor_StringInt_CanSend() { @@ -191,6 +185,21 @@ public void DisposeClose_OperationsThrow(bool close) Assert.Throws(() => udpClient.Send(null, 0, remoteEP)); Assert.Throws(() => udpClient.Send(null, 0)); Assert.Throws(() => udpClient.Send(null, 0, "localhost", 0)); + + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(), remoteEP)); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan())); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(), "localhost", 0)); + + Assert.Throws(() => {udpClient.SendAsync(null, 0, remoteEP);}); + Assert.Throws(() => {udpClient.SendAsync(null, 0);}); + Assert.Throws(() => {udpClient.SendAsync(null, 0, "localhost", 0);}); + + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(), remoteEP)); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory())); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(), "localhost", 0)); + + Assert.Throws(() => {udpClient.ReceiveAsync();}); + Assert.Throws(() => udpClient.ReceiveAsync(default)); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] @@ -321,7 +330,6 @@ public void InvalidArguments_Throw() } } - [OuterLoop] [Fact] public void BeginSend_NegativeBytes_Throws() { @@ -337,7 +345,6 @@ public void BeginSend_NegativeBytes_Throws() } } - [OuterLoop] [Fact] public void BeginSend_BytesMoreThanArrayLength_Throws() { @@ -353,7 +360,6 @@ public void BeginSend_BytesMoreThanArrayLength_Throws() } } - [OuterLoop] [Fact] public void BeginSend_AsyncOperationCompletes_Success() { @@ -377,8 +383,12 @@ public void Send_InvalidArguments_Throws() AssertExtensions.Throws("dgram", () => udpClient.Send(null, 0, "localhost", 0)); AssertExtensions.Throws("dgram", () => udpClient.Send(null, 0, new IPEndPoint(IPAddress.Loopback, 0))); Assert.Throws(() => udpClient.Send(new byte[1], 1)); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1]))); udpClient.Active = true; Assert.Throws(() => udpClient.Send(new byte[1], 1, new IPEndPoint(IPAddress.Loopback, 0))); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1]), new IPEndPoint(IPAddress.Loopback, 0))); + Assert.Throws(() => {udpClient.SendAsync(new byte[1], 1, new IPEndPoint(IPAddress.Loopback, 0));}); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), new IPEndPoint(IPAddress.Loopback, 0))); } } @@ -389,10 +399,17 @@ public void Send_InvalidArguments_StringInt_Throws() using (var udpClient = new UdpClient("localhost", 0)) { Assert.Throws(() => udpClient.Send(new byte[1], 1, "localhost", 0)); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1]), "localhost", 0)); + Assert.Throws(() => {udpClient.SendAsync(new byte[1], 1, "localhost", 0);}); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), "localhost", 0)); + + Assert.Throws(() => udpClient.Send(new byte[1], 1, null, UnusedPort)); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1]), null, UnusedPort)); + Assert.Throws(() => {udpClient.SendAsync(new byte[1], 1, null, UnusedPort);}); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), null, UnusedPort)); } } - [OuterLoop] [Fact] public void Client_Idempotent() { @@ -421,7 +438,6 @@ public void Connect_InvalidArguments_Throws() } } - [OuterLoop] [Fact] public async Task ConnectAsync_StringHost_Success() { @@ -431,7 +447,6 @@ public async Task ConnectAsync_StringHost_Success() } } - [OuterLoop] [Fact] public async Task ConnectAsync_IPAddressHost_Success() { @@ -441,7 +456,6 @@ public async Task ConnectAsync_IPAddressHost_Success() } } - [OuterLoop] [Fact] public void Connect_StringHost_Success() { @@ -451,7 +465,6 @@ public void Connect_StringHost_Success() } } - [OuterLoop] [Fact] public void Connect_IPAddressHost_Success() { @@ -468,7 +481,6 @@ private void AsyncCompleted(IAsyncResult ar) _waitHandle.Set(); } - [OuterLoop] [Theory] [PlatformSpecific(TestPlatforms.Windows)] // Udp.AllowNatTraversal only supported on Windows [InlineData(true, IPProtectionLevel.Unrestricted)] @@ -482,7 +494,6 @@ public void AllowNatTraversal_Windows(bool allow, IPProtectionLevel resultLevel) } } - [OuterLoop] [Theory] [PlatformSpecific(TestPlatforms.AnyUnix)] // Udp.AllowNatTraversal throws PNSE on Unix [InlineData(true)] @@ -495,7 +506,6 @@ public void AllowNatTraversal_AnyUnix(bool allow) } } - [OuterLoop] [Theory] [InlineData(false)] [InlineData(true)] @@ -507,32 +517,55 @@ public void Send_Receive_Success(bool ipv4) 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(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(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(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 @@ public void Send_Available_Success(bool ipv4) } } - [OuterLoop] [Theory] [InlineData(false)] [InlineData(true)] @@ -571,7 +603,6 @@ public void BeginEndSend_BeginEndReceive_Success(bool ipv4) [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 @@ public void BeginEndSend_BeginEndReceive_Connected_Success() } } - [OuterLoop] [Theory] [InlineData(false)] [InlineData(true)] @@ -598,28 +628,114 @@ public async Task SendAsync_ReceiveAsync_Success(bool ipv4) 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(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(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(() => 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(new byte[1])); + await AssertReceiveAsync(receiver); + + await sender.SendAsync(new ReadOnlyMemory(new byte[1]), null); + await AssertReceiveAsync(receiver); + + await sender.SendAsync(new ReadOnlyMemory(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(() => sender.SendAsync(new ReadOnlyMemory(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(() => sender.SendAsync(new ReadOnlyMemory(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(() => sender.SendAsync(new ReadOnlyMemory(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port), new CancellationToken(true)).AsTask()); } }