runtime: Type and content inconsistency between TcpClient.Client.LocalEndPoint/RemoteEndPoint and TcpConnectionInformation.LocalEndPoint/RemoteEndPoint

Description

When attempting to identify a TCP connection from TcpClient.Client.LocalEndPoint or RemoteEndPoint using the TcpConnectionInformation[] returned from IPGlobalProperties.GetIPGlobalProperties.GetActiveTcpConnections(), there is a material difference between the contents of LocalEndPoint and RemoteEndPoint.

On a Windows 11 host with both IPv4 and IPv6 enabled, .ToString() for the TcpClient returns this:

Console.WriteLine(client.Client.LocalEndPoint.ToString());
// [::ffff:127.0.0.1]:55853

Whereas after retrieving the connection information, it returns something different:

Console.WriteLine(conn.LocalEndPoint.ToString() + " to " + conn.RemoteEndPoint.ToString() + ": " + conn.State.ToString());
// 127.0.0.1:55853 to 127.0.0.1:8000: Established

It appears TcpClient contains a hybrid IPv6/IPv4 address whereas TcpConnectionInformation only contains IPv4.

Is there a way to identify the connection from TcpConnectionInformation in a consistent manner, preferably across runtimes and across platforms?

Reproduction Steps

Documented here: https://stackoverflow.com/questions/76979491/matching-tcp-connections-between-tcpclient-and-tcpconnectioninformation and also above

Expected behavior

LocalEndPoint and RemoteEndPoint would contain values of similar structure and be comparable, between TcpClient and TcpConnectionInformation

Actual behavior

TcpClient contains a hybrid IPv6/IPv4 address whereas TcpConnectionInformation contains only an IPv4 address.

Regression?

Unknown

Known Workarounds

For localhost connections, just comparing by source port, which must be unique.

Configuration

.NET 7 Windows 11 x64 Unknown No Blazor

Other information

NA

About this issue

  • Original URL
  • State: closed
  • Created 10 months ago
  • Comments: 16 (8 by maintainers)

Most upvoted comments

This is because Socket has generic EndPoint that may or may not be IPEndPoint. For TCP you can do something like

        IPEndPoint lep = tcpClient.Client.LocalEndPoint as IPEndPoint;
        IPEndPoint rep = tcpClient.Client.RemoteEndPoint as IPEndPoint;

        Console.WriteLine("Client = {0} {1}", tcpClient.Client.LocalEndPoint, tcpClient.Client.RemoteEndPoint);
        Console.WriteLine("Client = {0} {1}", lep.Address.IsIPv4MappedToIPv6 ?
                                                new IPEndPoint(lep.Address.MapToIPv4(), lep.Port) : lep,
                                              rep.Address.IsIPv4MappedToIPv6 ?
                                                new IPEndPoint(rep.Address.MapToIPv4(), lep.Port) : rep);

and it would produce something like

Client = [::ffff:127.0.0.1]:47868 [::ffff:127.0.0.1]:36935
Client = 127.0.0.1:47868 127.0.0.1:47868

TcpConnectionInformation already gives you IPEndPoint so the cast is not needed. Once converted, IPEndPoint.Equals should just work without need to convert to string. If you compare Address and Port separately, you can avoid allocation of the new IPEndPoint but that may be small benefit.

I hope this make some sense @jchristn.

That is property of IPAddres e.g. conn.LocalEndPoint.Address