【问题标题】:c# Know with which IP we use to communicate with another IP?c# 知道我们使用哪个 IP 与另一个 IP 通信吗?
【发布时间】:2013-02-26 10:36:23
【问题描述】:

通过自制的发现工具,我发现了我感兴趣的服务列表。

我有他们的 IP、服务名称、端口、主机……但要使用它们,我必须向客户端库指定我们将使用的 IP。

由于我有几块网卡,所以我需要检测使用哪个接口与我知道的目标 IP 进行通信,然后将这个IPAddress 提供给我的库。

但是我如何检测我应该使用哪个接口 IP?

我尝试在互联网上进行一些搜索,但我认为我没有正确的关键字,因为我没有找到任何相关的内容。

【问题讨论】:

标签: c# .net network-programming


【解决方案1】:

我已经从我开发的网络库networkComms.net中为你提取了相关方法:

/// <summary>
/// Determines the most appropriate local end point to contact the provided remote end point. 
/// Testing shows this method takes on average 1.6ms to return.
/// </summary>
/// <param name="remoteIPEndPoint">The remote end point</param>
/// <returns>The selected local end point</returns>
public static IPEndPoint BestLocalEndPoint(IPEndPoint remoteIPEndPoint)
{    
    Socket testSocket = new Socket(remoteIPEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
    testSocket.Connect(remoteIPEndPoint);
    return (IPEndPoint)testSocket.LocalEndPoint;
}

一旦您拥有正确的 IP,您就可以遍历 NetworkInterface.GetAllNetworkInterfaces() 以找到匹配的适配器。

【讨论】:

  • 太好了,如果我们有多个端点允许我 ping 我的远程 IP,那么如何选择端点?选择最快的?
  • 由基础操作系统决定。操作系统通常会使用路由表做出此决定,特别是路由器指标 (en.wikipedia.org/wiki/Router_metrics)。您可以通过自己访问路由表来强制做出手动决定,就像在另一个答案中一样,但这通常比它的价值更麻烦。
【解决方案2】:

以下通过查询路由表来工作。这与Socket.Connect 确定要使用的本地端点的方式相同。差异:

  • 不能因为防火墙而失败
  • 由于远程端点不存在而不能失败
  • 不保留本地端口
  • 更快

.

private static IPEndPoint QueryRoutingInterface(
          Socket socket,
          IPEndPoint remoteEndPoint)
{
    SocketAddress address = remoteEndPoint.Serialize();

    byte[] remoteAddrBytes = new byte[address.Size];
    for (int i = 0; i < address.Size; i++) {
        remoteAddrBytes[i] = address[i];
    }

    byte[] outBytes = new byte[remoteAddrBytes.Length];
    socket.IOControl(
                IOControlCode.RoutingInterfaceQuery, 
                remoteAddrBytes, 
                outBytes);
    for (int i = 0; i < address.Size; i++) {
        address[i] = outBytes[i];
    }

    EndPoint ep = remoteEndPoint.Create(address);
    return (IPEndPoint)ep;
}

使用如下:

IPAddress remoteIp = IPAddress.Parse("192.168.1.55");
IpEndPoint remoteEndPoint = new IPEndPoint(remoteIp, 0);
Socket socket = new Socket(
                      AddressFamily.InterNetwork, 
                      SocketType.Dgram, 
                      ProtocolType.Udp);
IPEndPoint localEndPoint = QueryRoutingInterface(socket, remoteEndPoint );
Console.WriteLine("Local EndPoint is: {0}", localEndPoint);

(源代码复制自here

请注意,尽管使用端口指定IpEndPoint,但端口无关紧要。此外,返回的IpEndPoint.Port 始终为0

【讨论】:

    【解决方案3】:

    我花了几个小时来找到这个问题的简短答案,感谢 BatteryBackupUnit,我已经做出了这个解决方案,希望它可以帮助其他程序员:

        [DllImport("ws2_32.dll", SetLastError = true)]
        private static extern SocketError WSAIoctl([In] IntPtr socketHandle, uint ioControlCode, [In] byte[] inBuffer, int inBufferSize, [Out] byte[] outBuffer, int outBufferSize, out int bytesTransferred, IntPtr overlapped, IntPtr completionRoutine);
    
        /// <summary>Get local IP-address for remote address</summary>
        /// <param name="remoteAddress">Remote Address</param>
        /// <returns></returns>
        public static IPAddress GetLocalAddressForRemote(IPAddress remoteAddress)
        {
            if (remoteAddress == null) return null;
    
            long rm = remoteAddress.Address;
            //Temporary socket only for handle of it
            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            byte[] src = new byte[16];
    
            src[0] = 2;
            src[4] = (byte)rm;
            src[5] = (byte)(rm >> 8);
            src[6] = (byte)(rm >> 16);
            src[7] = (byte)(rm >> 24);
    
            int transeferred = 0;
            if (WSAIoctl(s.Handle, 3355443220, src, 16, src, 16, out transeferred, IntPtr.Zero, IntPtr.Zero) == SocketError.Success)
            {
                s.Dispose();
                rm = src[4];
                rm |= ((long)src[5]) << 8;
                rm |= ((long)src[6]) << 16;
                rm |= ((long)src[7]) << 24;
                return new IPAddress(rm);
            }
            s.Dispose();
            return null;
        }
    

    【讨论】:

      猜你喜欢
      • 2020-06-07
      • 2012-05-25
      • 1970-01-01
      • 1970-01-01
      • 2016-01-26
      • 2022-12-11
      • 1970-01-01
      • 1970-01-01
      • 2014-07-15
      相关资源
      最近更新 更多