From f05bfbf7cac196030e02857a5f49e1fdd23bbe43 Mon Sep 17 00:00:00 2001 From: lateapexearlyspeed Date: Fri, 28 May 2021 23:43:48 +0800 Subject: [PATCH 1/9] Initial commit: Send() with span. --- .../System.Net.Sockets/ref/System.Net.Sockets.cs | 1 + .../src/System/Net/Sockets/UDPClient.cs | 14 ++++++++++++++ 2 files changed, 15 insertions(+) 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..dbb4933edccd1 100644 --- a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs +++ b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs @@ -757,6 +757,7 @@ public void JoinMulticastGroup(System.Net.IPAddress multicastAddr, System.Net.IP public byte[] Receive([System.Diagnostics.CodeAnalysis.NotNullAttribute] ref System.Net.IPEndPoint? remoteEP) { throw null; } public System.Threading.Tasks.Task ReceiveAsync() { throw null; } public int Send(byte[] dgram, int bytes) { throw null; } + public int Send(ReadOnlySpan dgram) {throw null; } public int Send(byte[] dgram, int bytes, System.Net.IPEndPoint? endPoint) { throw null; } public int Send(byte[] dgram, int bytes, string? hostname, int port) { throw null; } public System.Threading.Tasks.Task SendAsync(byte[] datagram, int bytes) { throw null; } 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..506f0d044f8da 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 @@ -950,6 +950,20 @@ public int Send(byte[] dgram, int bytes) return Client.Send(dgram, 0, bytes, SocketFlags.None); } + // Sends a UDP datagram to a remote host. + public int Send(ReadOnlySpan dgram) + { + ThrowIfDisposed(); + + if (!_active) + { + // only allowed on connected socket + throw new InvalidOperationException(SR.net_notconnected); + } + + return Client.Send(dgram, SocketFlags.None); + } + private void ThrowIfDisposed() { if (_disposed) From e5cd9219157ae4296f6ff51fabd4af613e52b552 Mon Sep 17 00:00:00 2001 From: lateapexearlyspeed Date: Tue, 1 Jun 2021 17:39:12 +0800 Subject: [PATCH 2/9] Add other send methods; refactor code by reusing GetEndpoint() for (hostname, port) overloads. --- .../ref/System.Net.Sockets.cs | 6 +- .../src/System/Net/Sockets/UDPClient.cs | 71 ++++++++++++------- 2 files changed, 49 insertions(+), 28 deletions(-) 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 dbb4933edccd1..1e983408badae 100644 --- a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs +++ b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs @@ -757,11 +757,15 @@ public void JoinMulticastGroup(System.Net.IPAddress multicastAddr, System.Net.IP public byte[] Receive([System.Diagnostics.CodeAnalysis.NotNullAttribute] ref System.Net.IPEndPoint? remoteEP) { throw null; } public System.Threading.Tasks.Task ReceiveAsync() { throw null; } public int Send(byte[] dgram, int bytes) { throw null; } - public int Send(ReadOnlySpan dgram) {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 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 506f0d044f8da..ab38ed1f9b7d3 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 { @@ -334,6 +335,17 @@ private void ValidateDatagram(byte[] datagram, int bytes, IPEndPoint? endPoint) } } + private void ValidateDatagram(ReadOnlyMemory datagram, IPEndPoint? endPoint) + { + ThrowIfDisposed(); + + if (_active && endPoint != null) + { + // Do not allow sending packets to arbitrary host when connected. + throw new InvalidOperationException(SR.net_udpconnected); + } + } + private IPEndPoint? GetEndpoint(string? hostname, int port) { if (_active && ((hostname != null) || (port != 0))) @@ -600,6 +612,9 @@ public void DropMulticastGroup(IPAddress multicastAddr, int ifindex) public Task SendAsync(byte[] datagram, int bytes) => SendAsync(datagram, bytes, null); + 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)); @@ -618,6 +633,21 @@ public Task SendAsync(byte[] datagram, int bytes, IPEndPoint? endPoint) } } + public ValueTask SendAsync(ReadOnlyMemory datagram, IPEndPoint? endPoint, CancellationToken cancellationToken = default) + { + ValidateDatagram(datagram, endPoint); + + if (endPoint is null) + { + return _clientSocket.SendAsync(datagram, SocketFlags.None, cancellationToken); + } + else + { + CheckForBroadcast(endPoint.Address); + return _clientSocket.SendToAsync(datagram, SocketFlags.None, endPoint, cancellationToken); + } + } + public Task ReceiveAsync() { ThrowIfDisposed(); @@ -892,45 +922,32 @@ 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 remote end point. + 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 the specified port on the specified remote host. + 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) @@ -951,7 +968,7 @@ public int Send(byte[] dgram, int bytes) } // Sends a UDP datagram to a remote host. - public int Send(ReadOnlySpan dgram) + public int Send(ReadOnlySpan datagram) { ThrowIfDisposed(); @@ -961,7 +978,7 @@ public int Send(ReadOnlySpan dgram) throw new InvalidOperationException(SR.net_notconnected); } - return Client.Send(dgram, SocketFlags.None); + return Client.Send(datagram, SocketFlags.None); } private void ThrowIfDisposed() From 32050dafe03c496dbb8b4d6c25e97b2844592727 Mon Sep 17 00:00:00 2001 From: lateapexearlyspeed Date: Thu, 3 Jun 2021 19:17:12 +0800 Subject: [PATCH 3/9] Add ReceiveAsync(). --- .../ref/System.Net.Sockets.cs | 1 + .../src/System/Net/Sockets/UDPClient.cs | 21 +++++++++++++++++++ .../System/Net/Sockets/UdpReceiveResult.cs | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) 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 1e983408badae..912b10d5daca1 100644 --- a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs +++ b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs @@ -756,6 +756,7 @@ 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; } 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 ab38ed1f9b7d3..13c2dd977031f 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 @@ -669,6 +669,27 @@ async Task WaitAndWrap(Task task) } } + 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. 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 { From d9c5e2b32096cb5f286820372a9da28953af7ab9 Mon Sep 17 00:00:00 2001 From: lateapexearlyspeed Date: Fri, 4 Jun 2021 18:41:03 +0800 Subject: [PATCH 4/9] Add method 'SendAsync(System.ReadOnlyMemory datagram, string? hostname, int port ...); Add test. --- .../ref/System.Net.Sockets.cs | 1 + .../src/System/Net/Sockets/UDPClient.cs | 3 + .../SendReceive/SendReceiveUdpClient.cs | 73 +++++++++++++++++++ .../tests/FunctionalTests/UdpClientTest.cs | 72 +++++++++++++++++- 4 files changed, 146 insertions(+), 3 deletions(-) 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 912b10d5daca1..721a083b3ec18 100644 --- a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs +++ b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs @@ -768,6 +768,7 @@ public void JoinMulticastGroup(System.Net.IPAddress multicastAddr, System.Net.IP 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 13c2dd977031f..fe00cd91a6426 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 @@ -618,6 +618,9 @@ public ValueTask SendAsync(ReadOnlyMemory datagram, CancellationToken public Task SendAsync(byte[] datagram, int bytes, string? hostname, int port) => SendAsync(datagram, bytes, GetEndpoint(hostname, port)); + 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); 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..efe8e336f1411 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs @@ -85,5 +85,78 @@ public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackA } } } + + [OuterLoop] + [Theory] + [MemberData(nameof(Loopbacks))] + public async Task SendToRecvFromAsyncWithReadOnlyMemory_Datagram_UDP_UdpClient(IPAddress loopbackAddress) + { + IPAddress leftAddress = loopbackAddress, rightAddress = loopbackAddress; + + const int DatagramSize = 256; + const int DatagramsToSend = 256; + const int AckTimeout = 20000; + const int TestTimeout = 60000; + + using (var left = new UdpClient(new IPEndPoint(leftAddress, 0))) + using (var right = new UdpClient(new IPEndPoint(rightAddress, 0))) + { + var leftEndpoint = (IPEndPoint)left.Client.LocalEndPoint; + var rightEndpoint = (IPEndPoint)right.Client.LocalEndPoint; + + var receiverAck = new ManualResetEventSlim(); + + var receivedChecksums = new uint?[DatagramsToSend]; + int receivedDatagrams = 0; + + Task receiverTask = Task.Run(async () => + { + for (; receivedDatagrams < DatagramsToSend; receivedDatagrams++) + { + UdpReceiveResult result = await left.ReceiveAsync(default); + + receiverAck.Set(); + + Assert.Equal(DatagramSize, result.Buffer.Length); + Assert.Equal(rightEndpoint, result.RemoteEndPoint); + + int datagramId = (int)result.Buffer[0]; + Assert.Null(receivedChecksums[datagramId]); + + receivedChecksums[datagramId] = Fletcher32.Checksum(result.Buffer, 0, result.Buffer.Length); + } + }); + + var sentChecksums = new uint[DatagramsToSend]; + int sentDatagrams = 0; + + Task senderTask = Task.Run(async () => + { + var random = new Random(); + var sendBuffer = new byte[DatagramSize]; + + for (; sentDatagrams < DatagramsToSend; sentDatagrams++) + { + random.NextBytes(sendBuffer); + sendBuffer[0] = (byte)sentDatagrams; + + int sent = await right.SendAsync(new ReadOnlyMemory(sendBuffer), leftEndpoint); + + Assert.True(receiverAck.Wait(AckTimeout)); + receiverAck.Reset(); + + Assert.Equal(DatagramSize, sent); + sentChecksums[sentDatagrams] = Fletcher32.Checksum(sendBuffer, 0, sent); + } + }); + + await (new[] { receiverTask, senderTask }).WhenAllOrAnyFailed(TestTimeout); + for (int i = 0; i < DatagramsToSend; i++) + { + Assert.NotNull(receivedChecksums[i]); + Assert.Equal(sentChecksums[i], (uint)receivedChecksums[i]); + } + } + } } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs index 1137613a062c1..983a3654f15b4 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs @@ -156,7 +156,7 @@ public void Ctor_StringInt_CanSend() [Theory] [InlineData(false)] [InlineData(true)] - public void DisposeClose_OperationsThrow(bool close) + public async Task DisposeClose_OperationsThrow(bool close) { var udpClient = new UdpClient(); @@ -191,6 +191,16 @@ 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)); + + await Assert.ThrowsAsync(() => udpClient.SendAsync(new ReadOnlyMemory(), remoteEP).AsTask()); + await Assert.ThrowsAsync(() => udpClient.SendAsync(new ReadOnlyMemory()).AsTask()); + await Assert.ThrowsAsync(() => udpClient.SendAsync(new ReadOnlyMemory(), "localhost", 0).AsTask()); + + await Assert.ThrowsAsync(() => udpClient.ReceiveAsync(default).AsTask()); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] @@ -369,7 +379,7 @@ public void BeginSend_AsyncOperationCompletes_Success() } [Fact] - public void Send_InvalidArguments_Throws() + public async Task Send_InvalidArguments_Throws() { using (var udpClient = new DerivedUdpClient()) { @@ -377,18 +387,23 @@ 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))); + await Assert.ThrowsAsync(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), new IPEndPoint(IPAddress.Loopback, 0)).AsTask()); } } [Fact] [PlatformSpecific(TestPlatforms.Windows)] // localhost on Windows resolves to both IPV4/6, but doesn't on all Unix - public void Send_InvalidArguments_StringInt_Throws() + public async Task 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)); + await Assert.ThrowsAsync(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), "localhost", 0).AsTask()); } } @@ -606,6 +621,40 @@ public async Task SendAsync_ReceiveAsync_Success(bool ipv4) } } + [OuterLoop] + [Theory] + [InlineData(false)] + [InlineData(true)] + public async ValueTask SendAsyncWithReadOnlyMemory_ReceiveAsync_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 ReadOnlyMemory(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port)); + + UdpReceiveResult result = await receiver.ReceiveAsync(default); + 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 + [OuterLoop] + public async Task ReceiveAsync_Cancel_Throw() + { + using (var receiver = new UdpClient("localhost", 0)) + { + using (var timeoutCts = new CancellationTokenSource(1)) + { + await Assert.ThrowsAsync(() => 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] @@ -623,6 +672,23 @@ public async Task SendAsync_ReceiveAsync_Connected_Success() } } + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix + [OuterLoop] + public async ValueTask SendAsyncWithReadOnlyMemory_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 ReadOnlyMemory(new byte[1])); + + UdpReceiveResult result = await receiver.ReceiveAsync(default); + Assert.NotNull(result.RemoteEndPoint); + Assert.NotNull(result.Buffer); + Assert.InRange(result.Buffer.Length, 1, int.MaxValue); + } + } + [Fact] public void JoinDropMulticastGroup_InvalidArguments_Throws() { From d8e144cbe5a9e0fbf18b8a7a8d763101bafbbf42 Mon Sep 17 00:00:00 2001 From: lateapexearlyspeed Date: Fri, 11 Jun 2021 15:26:51 +0800 Subject: [PATCH 5/9] Fix comment: Add xml doc for new APIs. --- .../src/System/Net/Sockets/UDPClient.cs | 98 ++++++++++++++++++- 1 file changed, 95 insertions(+), 3 deletions(-) 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 fe00cd91a6426..6271854fdcae3 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 @@ -612,12 +612,43 @@ 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); @@ -636,6 +667,22 @@ 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) { ValidateDatagram(datagram, endPoint); @@ -672,6 +719,15 @@ 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(); @@ -946,7 +1002,19 @@ public int Send(byte[] dgram, int bytes, IPEndPoint? endPoint) return Client.SendTo(dgram, 0, bytes, SocketFlags.None, endPoint); } - // Sends a UDP datagram to the host at the remote end point. + /// + /// 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(); @@ -970,7 +1038,22 @@ public int Send(ReadOnlySpan datagram, IPEndPoint? 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 the specified port on the specified remote host. + /// + /// 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. @@ -991,7 +1074,16 @@ public int Send(byte[] dgram, int bytes) return Client.Send(dgram, 0, bytes, SocketFlags.None); } - // Sends a UDP datagram to a remote host. + /// + /// 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(); From 05b0cf2cd50fe80f1ed43c2d6b9edf94f7a2b05c Mon Sep 17 00:00:00 2001 From: lateapexearlyspeed Date: Tue, 15 Jun 2021 18:15:10 +0800 Subject: [PATCH 6/9] Fix comment: Refine test codes. --- .../src/System/Net/Sockets/UDPClient.cs | 21 +-- .../SendReceive/SendReceiveUdpClient.cs | 83 +-------- .../tests/FunctionalTests/TelemetryTest.cs | 4 +- .../tests/FunctionalTests/UdpClientTest.cs | 170 +++++++++--------- 4 files changed, 105 insertions(+), 173 deletions(-) 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 6271854fdcae3..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 @@ -335,17 +335,6 @@ private void ValidateDatagram(byte[] datagram, int bytes, IPEndPoint? endPoint) } } - private void ValidateDatagram(ReadOnlyMemory datagram, IPEndPoint? endPoint) - { - ThrowIfDisposed(); - - if (_active && endPoint != null) - { - // Do not allow sending packets to arbitrary host when connected. - throw new InvalidOperationException(SR.net_udpconnected); - } - } - private IPEndPoint? GetEndpoint(string? hostname, int port) { if (_active && ((hostname != null) || (port != 0))) @@ -685,17 +674,19 @@ public Task SendAsync(byte[] datagram, int bytes, IPEndPoint? endPoint) /// An error occurred when accessing the socket. public ValueTask SendAsync(ReadOnlyMemory datagram, IPEndPoint? endPoint, CancellationToken cancellationToken = default) { - ValidateDatagram(datagram, endPoint); + ThrowIfDisposed(); if (endPoint is null) { return _clientSocket.SendAsync(datagram, SocketFlags.None, cancellationToken); } - else + if (_active) { - CheckForBroadcast(endPoint.Address); - return _clientSocket.SendToAsync(datagram, SocketFlags.None, endPoint, cancellationToken); + // 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() 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 efe8e336f1411..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(); @@ -86,77 +86,12 @@ public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackA } } - [OuterLoop] - [Theory] - [MemberData(nameof(Loopbacks))] - public async Task SendToRecvFromAsyncWithReadOnlyMemory_Datagram_UDP_UdpClient(IPAddress loopbackAddress) + public static readonly object[][] LoopbacksAndUseMemory = new object[][] { - IPAddress leftAddress = loopbackAddress, rightAddress = loopbackAddress; - - const int DatagramSize = 256; - const int DatagramsToSend = 256; - const int AckTimeout = 20000; - const int TestTimeout = 60000; - - using (var left = new UdpClient(new IPEndPoint(leftAddress, 0))) - using (var right = new UdpClient(new IPEndPoint(rightAddress, 0))) - { - var leftEndpoint = (IPEndPoint)left.Client.LocalEndPoint; - var rightEndpoint = (IPEndPoint)right.Client.LocalEndPoint; - - var receiverAck = new ManualResetEventSlim(); - - var receivedChecksums = new uint?[DatagramsToSend]; - int receivedDatagrams = 0; - - Task receiverTask = Task.Run(async () => - { - for (; receivedDatagrams < DatagramsToSend; receivedDatagrams++) - { - UdpReceiveResult result = await left.ReceiveAsync(default); - - receiverAck.Set(); - - Assert.Equal(DatagramSize, result.Buffer.Length); - Assert.Equal(rightEndpoint, result.RemoteEndPoint); - - int datagramId = (int)result.Buffer[0]; - Assert.Null(receivedChecksums[datagramId]); - - receivedChecksums[datagramId] = Fletcher32.Checksum(result.Buffer, 0, result.Buffer.Length); - } - }); - - var sentChecksums = new uint[DatagramsToSend]; - int sentDatagrams = 0; - - Task senderTask = Task.Run(async () => - { - var random = new Random(); - var sendBuffer = new byte[DatagramSize]; - - for (; sentDatagrams < DatagramsToSend; sentDatagrams++) - { - random.NextBytes(sendBuffer); - sendBuffer[0] = (byte)sentDatagrams; - - int sent = await right.SendAsync(new ReadOnlyMemory(sendBuffer), leftEndpoint); - - Assert.True(receiverAck.Wait(AckTimeout)); - receiverAck.Reset(); - - Assert.Equal(DatagramSize, sent); - sentChecksums[sentDatagrams] = Fletcher32.Checksum(sendBuffer, 0, sent); - } - }); - - await (new[] { receiverTask, senderTask }).WhenAllOrAnyFailed(TestTimeout); - for (int i = 0; i < DatagramsToSend; i++) - { - Assert.NotNull(receivedChecksums[i]); - Assert.Equal(sentChecksums[i], (uint)receivedChecksums[i]); - } - } - } + 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 983a3654f15b4..94ffa4a75ab47 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() { @@ -156,7 +150,7 @@ public void Ctor_StringInt_CanSend() [Theory] [InlineData(false)] [InlineData(true)] - public async Task DisposeClose_OperationsThrow(bool close) + public void DisposeClose_OperationsThrow(bool close) { var udpClient = new UdpClient(); @@ -195,12 +189,17 @@ public async Task DisposeClose_OperationsThrow(bool close) 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);}); - await Assert.ThrowsAsync(() => udpClient.SendAsync(new ReadOnlyMemory(), remoteEP).AsTask()); - await Assert.ThrowsAsync(() => udpClient.SendAsync(new ReadOnlyMemory()).AsTask()); - await Assert.ThrowsAsync(() => udpClient.SendAsync(new ReadOnlyMemory(), "localhost", 0).AsTask()); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(), remoteEP)); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory())); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(), "localhost", 0)); - await Assert.ThrowsAsync(() => udpClient.ReceiveAsync(default).AsTask()); + Assert.Throws(() => {udpClient.ReceiveAsync();}); + Assert.Throws(() => udpClient.ReceiveAsync(default)); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] @@ -331,7 +330,6 @@ public void InvalidArguments_Throw() } } - [OuterLoop] [Fact] public void BeginSend_NegativeBytes_Throws() { @@ -347,7 +345,6 @@ public void BeginSend_NegativeBytes_Throws() } } - [OuterLoop] [Fact] public void BeginSend_BytesMoreThanArrayLength_Throws() { @@ -363,7 +360,6 @@ public void BeginSend_BytesMoreThanArrayLength_Throws() } } - [OuterLoop] [Fact] public void BeginSend_AsyncOperationCompletes_Success() { @@ -379,7 +375,7 @@ public void BeginSend_AsyncOperationCompletes_Success() } [Fact] - public async Task Send_InvalidArguments_Throws() + public void Send_InvalidArguments_Throws() { using (var udpClient = new DerivedUdpClient()) { @@ -391,23 +387,29 @@ public async Task Send_InvalidArguments_Throws() 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))); - await Assert.ThrowsAsync(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), new IPEndPoint(IPAddress.Loopback, 0)).AsTask()); + 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))); } } [Fact] [PlatformSpecific(TestPlatforms.Windows)] // localhost on Windows resolves to both IPV4/6, but doesn't on all Unix - public async Task Send_InvalidArguments_StringInt_Throws() + 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)); - await Assert.ThrowsAsync(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), "localhost", 0).AsTask()); + 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() { @@ -436,7 +438,6 @@ public void Connect_InvalidArguments_Throws() } } - [OuterLoop] [Fact] public async Task ConnectAsync_StringHost_Success() { @@ -446,7 +447,6 @@ public async Task ConnectAsync_StringHost_Success() } } - [OuterLoop] [Fact] public async Task ConnectAsync_IPAddressHost_Success() { @@ -456,7 +456,6 @@ public async Task ConnectAsync_IPAddressHost_Success() } } - [OuterLoop] [Fact] public void Connect_StringHost_Success() { @@ -466,7 +465,6 @@ public void Connect_StringHost_Success() } } - [OuterLoop] [Fact] public void Connect_IPAddressHost_Success() { @@ -483,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)] @@ -497,7 +494,6 @@ public void AllowNatTraversal_Windows(bool allow, IPProtectionLevel resultLevel) } } - [OuterLoop] [Theory] [PlatformSpecific(TestPlatforms.AnyUnix)] // Udp.AllowNatTraversal throws PNSE on Unix [InlineData(true)] @@ -510,7 +506,6 @@ public void AllowNatTraversal_AnyUnix(bool allow) } } - [OuterLoop] [Theory] [InlineData(false)] [InlineData(true)] @@ -522,32 +517,56 @@ 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); + + sender.Send(new ReadOnlySpan(new byte[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); + [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); - - IPEndPoint remoteEP = null; - byte[] data = receiver.Receive(ref remoteEP); - Assert.NotNull(remoteEP); - Assert.InRange(data.Length, 1, int.MaxValue); + AssertReceive(receiver); + + 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)] @@ -564,7 +583,6 @@ public void Send_Available_Success(bool ipv4) } } - [OuterLoop] [Theory] [InlineData(false)] [InlineData(true)] @@ -586,7 +604,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)) @@ -601,7 +618,6 @@ public void BeginEndSend_BeginEndReceive_Connected_Success() } } - [OuterLoop] [Theory] [InlineData(false)] [InlineData(true)] @@ -613,40 +629,40 @@ 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)); - - UdpReceiveResult result = await receiver.ReceiveAsync(); - Assert.NotNull(result.RemoteEndPoint); - Assert.NotNull(result.Buffer); - Assert.InRange(result.Buffer.Length, 1, int.MaxValue); + await AssertReceiveAsync(receiver); + + await sender.SendAsync(new ReadOnlyMemory(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port)); + await AssertReceiveAsync(receiver); } } - [OuterLoop] [Theory] [InlineData(false)] [InlineData(true)] - public async ValueTask SendAsyncWithReadOnlyMemory_ReceiveAsync_Success(bool ipv4) + [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 ReadOnlyMemory(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port)); - - UdpReceiveResult result = await receiver.ReceiveAsync(default); - Assert.NotNull(result.RemoteEndPoint); - Assert.NotNull(result.Buffer); - Assert.InRange(result.Buffer.Length, 1, int.MaxValue); + 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); } } - [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 ReceiveAsync_Cancel_Throw() + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task ReceiveAsync_Cancel_Throw(bool ipv4) { - using (var receiver = new UdpClient("localhost", 0)) + IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback; + + using (var receiver = new UdpClient(new IPEndPoint(address, 0))) { using (var timeoutCts = new CancellationTokenSource(1)) { @@ -657,37 +673,27 @@ public async Task ReceiveAsync_Cancel_Throw() [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); - - 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 - [OuterLoop] - public async ValueTask SendAsyncWithReadOnlyMemory_ReceiveAsync_Connected_Success() - { - using (var receiver = new UdpClient("localhost", 0)) - using (var sender = new UdpClient("localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port)) - { + await AssertReceiveAsync(receiver); + await sender.SendAsync(new ReadOnlyMemory(new byte[1])); - - UdpReceiveResult result = await receiver.ReceiveAsync(default); - Assert.NotNull(result.RemoteEndPoint); - Assert.NotNull(result.Buffer); - Assert.InRange(result.Buffer.Length, 1, int.MaxValue); - } - } + 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] public void JoinDropMulticastGroup_InvalidArguments_Throws() From 14465d02379de9c351f901385a8c2dd3fae7f4e6 Mon Sep 17 00:00:00 2001 From: lateapexearlyspeed Date: Tue, 15 Jun 2021 18:28:42 +0800 Subject: [PATCH 7/9] Remove tab. --- .../tests/FunctionalTests/UdpClientTest.cs | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs index 94ffa4a75ab47..f2ccbc071b4c9 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs @@ -518,11 +518,10 @@ public void Send_Receive_Success(bool ipv4) { sender.Send(new byte[1], 1, new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port)); AssertReceive(receiver); - + sender.Send(new ReadOnlySpan(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port)); AssertReceive(receiver); } - } [Theory] @@ -538,7 +537,7 @@ public void Send_Receive_With_HostName_Success(bool ipv4) { 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); } @@ -553,7 +552,7 @@ public void Send_Receive_Connected_Success() { sender.Send(new byte[1], 1); AssertReceive(receiver); - + sender.Send(new ReadOnlySpan(new byte[1])); AssertReceive(receiver); } @@ -648,10 +647,10 @@ public async Task SendAsync_ReceiveAsync_With_HostName_Success(bool ipv4) 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); + await AssertReceiveAsync(receiver); + + await sender.SendAsync(new ReadOnlyMemory(new byte[1]), "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port); + await AssertReceiveAsync(receiver); } } @@ -679,21 +678,20 @@ public async Task SendAsync_ReceiveAsync_Connected_Success() using (var sender = new UdpClient("localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port)) { await sender.SendAsync(new byte[1], 1); - await AssertReceiveAsync(receiver); - + await AssertReceiveAsync(receiver); + await sender.SendAsync(new ReadOnlyMemory(new byte[1])); - await AssertReceiveAsync(receiver); + await AssertReceiveAsync(receiver); } } - - private static async Task AssertReceiveAsync(UdpClient receiver) - { - UdpReceiveResult result = await receiver.ReceiveAsync(); + + 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] public void JoinDropMulticastGroup_InvalidArguments_Throws() From 0ca0fee6b2078e922d815ba871047d1158d3780a Mon Sep 17 00:00:00 2001 From: lateapexearlyspeed Date: Thu, 17 Jun 2021 15:50:11 +0800 Subject: [PATCH 8/9] Use ThrowsAnyAsync() to assert. --- .../System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs index f2ccbc071b4c9..2e5d46bae52bd 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs @@ -665,7 +665,7 @@ public async Task ReceiveAsync_Cancel_Throw(bool ipv4) { using (var timeoutCts = new CancellationTokenSource(1)) { - await Assert.ThrowsAsync(() => receiver.ReceiveAsync(timeoutCts.Token).AsTask()); + await Assert.ThrowsAnyAsync(() => receiver.ReceiveAsync(timeoutCts.Token).AsTask()); } } } From a368fff704ea2be841eb65c123ca233c150033cf Mon Sep 17 00:00:00 2001 From: lateapexearlyspeed Date: Sat, 19 Jun 2021 00:21:16 +0800 Subject: [PATCH 9/9] Fix comment: add more test for new SendAsync overloads. --- .../tests/FunctionalTests/UdpClientTest.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs index 2e5d46bae52bd..f4f967ca45ef9 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs @@ -682,6 +682,12 @@ public async Task SendAsync_ReceiveAsync_Connected_Success() 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); } } @@ -693,6 +699,46 @@ private static async Task AssertReceiveAsync(UdpClient receiver) 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()); + } + } + + [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()); + } + } + [Fact] public void JoinDropMulticastGroup_InvalidArguments_Throws() {