Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cache for Socket.LocalEndPoint #39313

Merged
merged 28 commits into from
Sep 2, 2020
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2e977ea
Add cache for LocalEndPoint
CarnaViire Jul 14, 2020
579f788
Add RightEndPoint property, clear _localEndPoint in RightEndPoint setter
CarnaViire Jul 14, 2020
bc08140
Merge remote-tracking branch 'upstream/master' into issue-1482
CarnaViire Jul 14, 2020
c68e608
Merge remote-tracking branch 'upstream/master' into issue-1482
CarnaViire Jul 20, 2020
3771a6d
Merge remote-tracking branch 'upstream/master' into issue-1482
CarnaViire Jul 23, 2020
d953b0f
Merge remote-tracking branch 'upstream/master' into issue-1482
CarnaViire Jul 23, 2020
f0a5ec9
Roll back property changes
CarnaViire Jul 23, 2020
999d596
Fix _nonBlockingConnectRightEndPoint
CarnaViire Jul 23, 2020
79b7153
Merge branch 'master' into issue-1482
CarnaViire Jul 31, 2020
d715a31
Add clear on error
CarnaViire Aug 4, 2020
c3eb4ec
Add clear on connect and tests
CarnaViire Aug 5, 2020
324e060
Add caching test
CarnaViire Aug 5, 2020
283f11b
Use BindToAnonymousPort
CarnaViire Aug 11, 2020
0a6a26a
Merge remote-tracking branch 'upstream/master' into issue-1482
CarnaViire Aug 11, 2020
1ff3e14
Fix typo
CarnaViire Aug 11, 2020
40f872e
Merge remote-tracking branch 'upstream/master' into issue-1482
CarnaViire Aug 11, 2020
09b42c0
Merge remote-tracking branch 'upstream/master' into issue-1482
CarnaViire Aug 12, 2020
24e1b66
Merge remote-tracking branch 'upstream/master' into issue-1482
CarnaViire Aug 18, 2020
ec8e54e
Assign _localEndPoint from the listener
CarnaViire Aug 18, 2020
3d9385b
tmp commit to check on mac
CarnaViire Aug 24, 2020
4e183ed
tmp
CarnaViire Aug 24, 2020
e626406
Merge remote-tracking branch 'upstream/master' into issue-1482
CarnaViire Aug 24, 2020
74988a2
Merge branch 'master' into issue-1482
CarnaViire Aug 25, 2020
eeb057d
Fix _localEndPoint clearing on mac
CarnaViire Aug 25, 2020
85de50d
Inline wildcard addresses
CarnaViire Aug 26, 2020
b86cc93
Merge remote-tracking branch 'upstream/master' into issue-1482
CarnaViire Aug 26, 2020
cbaed88
PR fixes
CarnaViire Aug 27, 2020
d0fa759
Rewrite tests with SocketTestHelperBase
CarnaViire Sep 1, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 35 additions & 10 deletions src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public partial class Socket : IDisposable
// _rightEndPoint is null if the socket has not been bound. Otherwise, it is any EndPoint of the
// correct type (IPEndPoint, etc).
internal EndPoint? _rightEndPoint;
private EndPoint? _localEndPoint; // Cached LocalEndPoint value. Will clear on connect, error and disconnect
internal EndPoint? _remoteEndPoint;

// These flags monitor if the socket was ever connected at any time and if it still is.
Expand Down Expand Up @@ -215,6 +216,7 @@ private unsafe Socket(SafeSocketHandle handle, bool loadPropertiesFromHandle)
}

_isConnected = true;
_localEndPoint = null;
break;

case SocketError.InvalidArgument:
Expand All @@ -224,6 +226,7 @@ private unsafe Socket(SafeSocketHandle handle, bool loadPropertiesFromHandle)
// whether we're actually connected or not, err on the side of saying
// we're connected.
_isConnected = true;
_localEndPoint = null;
break;
}
}
Expand Down Expand Up @@ -317,6 +320,7 @@ public int Available
// Update the state if we've become connected after a non-blocking connect.
_isConnected = true;
_rightEndPoint = _nonBlockingConnectRightEndPoint;
_localEndPoint = null;
_nonBlockingConnectInProgress = false;
}

Expand All @@ -325,23 +329,27 @@ public int Available
return null;
}

Internals.SocketAddress socketAddress = IPEndPointExtensions.Serialize(_rightEndPoint);

unsafe
if (_localEndPoint == null)
{
fixed (byte* buffer = socketAddress.Buffer)
fixed (int* bufferSize = &socketAddress.InternalSize)
Internals.SocketAddress socketAddress = IPEndPointExtensions.Serialize(_rightEndPoint);

unsafe
{
// This may throw ObjectDisposedException.
SocketError errorCode = SocketPal.GetSockName(_handle, buffer, bufferSize);
if (errorCode != SocketError.Success)
fixed (byte* buffer = socketAddress.Buffer)
fixed (int* bufferSize = &socketAddress.InternalSize)
{
UpdateStatusAfterSocketErrorAndThrowException(errorCode);
// This may throw ObjectDisposedException.
SocketError errorCode = SocketPal.GetSockName(_handle, buffer, bufferSize);
if (errorCode != SocketError.Success)
{
UpdateStatusAfterSocketErrorAndThrowException(errorCode);
}
}
}
_localEndPoint = _rightEndPoint.Create(socketAddress);
CarnaViire marked this conversation as resolved.
Show resolved Hide resolved
}

return _rightEndPoint.Create(socketAddress);
return _localEndPoint;
}
}

Expand All @@ -359,6 +367,7 @@ public int Available
// Update the state if we've become connected after a non-blocking connect.
_isConnected = true;
_rightEndPoint = _nonBlockingConnectRightEndPoint;
_localEndPoint = null;
_nonBlockingConnectInProgress = false;
}

Expand Down Expand Up @@ -470,6 +479,7 @@ public bool Connected
// Update the state if we've become connected after a non-blocking connect.
_isConnected = true;
_rightEndPoint = _nonBlockingConnectRightEndPoint;
_localEndPoint = null;
_nonBlockingConnectInProgress = false;
}

Expand Down Expand Up @@ -2303,6 +2313,7 @@ private void DoBeginDisconnect(bool reuseSocket, DisconnectOverlappedAsyncResult
{
SetToDisconnected();
_remoteEndPoint = null;
_localEndPoint = null;
}

if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"UnsafeNclNativeMethods.OSSOCK.DisConnectEx returns:{errorCode}");
Expand Down Expand Up @@ -2332,6 +2343,7 @@ public void Disconnect(bool reuseSocket)

SetToDisconnected();
_remoteEndPoint = null;
_localEndPoint = null;
}

// Routine Description:
Expand Down Expand Up @@ -2760,6 +2772,7 @@ private void DoBeginSendTo(byte[] buffer, int offset, int size, SocketFlags sock
catch (ObjectDisposedException)
{
_rightEndPoint = oldEndPoint;
_localEndPoint = null;
throw;
}

Expand All @@ -2769,6 +2782,7 @@ private void DoBeginSendTo(byte[] buffer, int offset, int size, SocketFlags sock
UpdateSendSocketErrorForDisposed(ref errorCode);
// Update the internal state of this socket according to the error before throwing.
_rightEndPoint = oldEndPoint;
_localEndPoint = null;

throw new SocketException((int)errorCode);
}
Expand Down Expand Up @@ -3148,6 +3162,7 @@ public IAsyncResult BeginReceiveMessageFrom(byte[] buffer, int offset, int size,
catch (ObjectDisposedException)
{
_rightEndPoint = oldEndPoint;
_localEndPoint = null;
throw;
}

Expand All @@ -3157,6 +3172,7 @@ public IAsyncResult BeginReceiveMessageFrom(byte[] buffer, int offset, int size,
{
// Update the internal state of this socket according to the error before throwing.
_rightEndPoint = oldEndPoint;
_localEndPoint = null;

throw new SocketException((int)errorCode);
}
Expand Down Expand Up @@ -3357,6 +3373,7 @@ private void DoBeginReceiveFrom(byte[] buffer, int offset, int size, SocketFlags
catch (ObjectDisposedException)
{
_rightEndPoint = oldEndPoint;
_localEndPoint = null;
throw;
}

Expand All @@ -3366,6 +3383,7 @@ private void DoBeginReceiveFrom(byte[] buffer, int offset, int size, SocketFlags
{
// Update the internal state of this socket according to the error before throwing.
_rightEndPoint = oldEndPoint;
_localEndPoint = null;

throw new SocketException((int)errorCode);
}
Expand Down Expand Up @@ -3762,6 +3780,7 @@ private bool ConnectAsync(SocketAsyncEventArgs e, bool userSocket)
catch
{
_rightEndPoint = oldEndPoint;
_localEndPoint = null;

// Clear in-use flag on event args object.
e.Complete();
Expand Down Expand Up @@ -4091,6 +4110,7 @@ public bool SendToAsync(SocketAsyncEventArgs e)
catch
{
_rightEndPoint = null;
_localEndPoint = null;
// Clear in-use flag on event args object.
e.Complete();
throw;
Expand All @@ -4099,6 +4119,7 @@ public bool SendToAsync(SocketAsyncEventArgs e)
if (!CheckErrorAndUpdateStatus(socketError))
{
_rightEndPoint = oldEndPoint;
_localEndPoint = null;
}

return socketError == SocketError.IOPending;
Expand Down Expand Up @@ -4610,6 +4631,7 @@ private IAsyncResult BeginConnectEx(EndPoint remoteEP, bool flowContext, AsyncCa
{
// _rightEndPoint will always equal oldEndPoint.
_rightEndPoint = oldEndPoint;
_localEndPoint = null;
throw;
}

Expand All @@ -4626,6 +4648,7 @@ private IAsyncResult BeginConnectEx(EndPoint remoteEP, bool flowContext, AsyncCa
UpdateConnectSocketErrorForDisposed(ref errorCode);
// Update the internal state of this socket according to the error before throwing.
_rightEndPoint = oldEndPoint;
_localEndPoint = null;

throw new SocketException((int)errorCode);
}
Expand Down Expand Up @@ -4848,6 +4871,7 @@ internal Socket UpdateAcceptSocket(Socket socket, EndPoint remoteEP)
socket._protocolType = _protocolType;
socket._rightEndPoint = _rightEndPoint;
socket._remoteEndPoint = remoteEP;
socket._localEndPoint = _localEndPoint;

// The socket is connected.
socket.SetToConnected();
Expand Down Expand Up @@ -4880,6 +4904,7 @@ internal void SetToConnected()
// some point in time update the perf counter as well.
_isConnected = true;
_isDisconnected = false;
_localEndPoint = null;
if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "now connected");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Threading.Tasks;
using Xunit;

namespace System.Net.Sockets.Tests
{
public class LocalEndPointTest
{
[Fact]
public void UdpSocket_BoundToWildcardAddress_LocalEPDoesNotChangeOnSendTo()
{
using (Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
{
int serverPort = server.BindToAnonymousPort(IPAddress.Any);

Assert.Null(client.LocalEndPoint);

int clientPortAfterBind = client.BindToAnonymousPort(IPAddress.Any);

Assert.Equal(IPAddress.Any, ((IPEndPoint)client.LocalEndPoint).Address);

var sendToEP = new IPEndPoint(IPAddress.Loopback, serverPort);

client.SendTo(new byte[] { 1, 2, 3 }, sendToEP);

Assert.Equal(IPAddress.Any, ((IPEndPoint)client.LocalEndPoint).Address);
Assert.Equal(clientPortAfterBind, ((IPEndPoint)client.LocalEndPoint).Port);

byte[] buf = new byte[3];
EndPoint receiveFromEP = new IPEndPoint(IPAddress.Any, 0);
server.ReceiveFrom(buf, ref receiveFromEP);

Assert.Equal(new byte[] { 1, 2, 3 }, buf);
Assert.Equal(clientPortAfterBind, ((IPEndPoint)receiveFromEP).Port);

IAsyncResult sendToResult = client.BeginSendTo(new byte[] { 4, 5, 6 }, 0, 3, SocketFlags.None, sendToEP, null, null);
sendToResult.AsyncWaitHandle.WaitOne();
client.EndSendTo(sendToResult);

Assert.Equal(IPAddress.Any, ((IPEndPoint)client.LocalEndPoint).Address);
Assert.Equal(clientPortAfterBind, ((IPEndPoint)client.LocalEndPoint).Port);

buf = new byte[3];
receiveFromEP = new IPEndPoint(IPAddress.Any, 0);
server.ReceiveFrom(buf, ref receiveFromEP);

Assert.Equal(new byte[] { 4, 5, 6 }, buf);
Assert.Equal(clientPortAfterBind, ((IPEndPoint)receiveFromEP).Port);
}
}

[Fact]
public void UdpSocket_NotBound_LocalEPBecomesWildcardAddressOnSendTo()
{
using (Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
{
int serverPort = server.BindToAnonymousPort(IPAddress.Any);

Assert.Null(client.LocalEndPoint);

var sendToEP = new IPEndPoint(IPAddress.Loopback, serverPort);

client.SendTo(new byte[] { 1, 2, 3 }, sendToEP);

Assert.Equal(IPAddress.Any, ((IPEndPoint)client.LocalEndPoint).Address);

byte[] buf = new byte[3];
EndPoint receiveFromEP = new IPEndPoint(IPAddress.Any, 0);
server.ReceiveFrom(buf, ref receiveFromEP);

Assert.Equal(new byte[] { 1, 2, 3 }, buf);

IAsyncResult sendToResult = client.BeginSendTo(new byte[] { 4, 5, 6 }, 0, 3, SocketFlags.None, sendToEP, null, null);
sendToResult.AsyncWaitHandle.WaitOne();
client.EndSendTo(sendToResult);

Assert.Equal(IPAddress.Any, ((IPEndPoint)client.LocalEndPoint).Address);

buf = new byte[3];
receiveFromEP = new IPEndPoint(IPAddress.Any, 0);
server.ReceiveFrom(buf, ref receiveFromEP);

Assert.Equal(new byte[] { 4, 5, 6 }, buf);
}
}

[Fact]
public async Task TcpSocket_BoundToWildcardAddress_LocalEPChangeToSpecificOnConnnect()
{
using (Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
int serverPort = server.BindToAnonymousPort(IPAddress.Any);
int clientPortAfterBind = client.BindToAnonymousPort(IPAddress.Any);

Assert.Equal(IPAddress.Any, ((IPEndPoint)client.LocalEndPoint).Address);

server.Listen();
Task<Socket> acceptTask = server.AcceptAsync();

client.Connect(new IPEndPoint(IPAddress.Loopback, serverPort));

Assert.Equal(IPAddress.Loopback, ((IPEndPoint)client.LocalEndPoint).Address);
Assert.Equal(clientPortAfterBind, ((IPEndPoint)client.LocalEndPoint).Port);

Socket accept = await acceptTask;
Assert.Equal(accept.RemoteEndPoint, client.LocalEndPoint);
}
}

[Fact]
public async Task TcpSocket_NotBound_LocalEPChangeToSpecificOnConnnect()
{
using (Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
int serverPort = server.BindToAnonymousPort(IPAddress.Any);
server.Listen();
Task<Socket> acceptTask = server.AcceptAsync();

Assert.Null(client.LocalEndPoint);

client.Connect(new IPEndPoint(IPAddress.Loopback, serverPort));

Assert.Equal(IPAddress.Loopback, ((IPEndPoint)client.LocalEndPoint).Address);

Socket accept = await acceptTask;
Assert.Equal(accept.RemoteEndPoint, client.LocalEndPoint);
}
}

[Fact]
public void LocalEndPoint_IsCached()
{
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
socket.BindToAnonymousPort(IPAddress.Any);

EndPoint localEndPointCall1 = socket.LocalEndPoint;
EndPoint localEndPointCall2 = socket.LocalEndPoint;

Assert.Same(localEndPointCall1, localEndPointCall2);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<Compile Include="IPPacketInformationTest.cs" />
<Compile Include="KeepAliveTest.cs" />
<Compile Include="LingerStateTest.cs" />
<Compile Include="LocalEndPointTest.cs" />
<Compile Include="LoggingTest.cs" />
<Compile Include="NetworkStreamTest.cs" />
<Compile Include="ReceiveMessageFrom.cs" />
Expand Down