如何为 HttpWebRequest 或 WebRequest C# 强制使用 ipv6 或 ipv4

问题描述 投票:0回答:4

来自node.js,我可以这样做来告诉node.js 使用 ipv6 与 ipv4 发出请求

var http = require("http");
var options = {
  hostname: "google.com",
  family: 4, // set to 6 for ipv6
};
var req = http.request(options, function(res) {
  .. handle result here ..
});
req.write("");
req.end();

family
设置为
4
会强制使用 ipv4,将其设置为
6
会强制使用 ipv6。不设置它就可以工作。

如何在 C# (.NET 3.5) 中做同样的事情

我可以想到一种方法,就是自己向 A 或 AAAA 记录发出 DNS 请求,发出直接 IP 请求并设置

host:
标头。有更好的办法吗?

c# network-programming ipv6 ipv4
4个回答
6
投票

您可以使用 ServicePoint.BindIPEndPointDelegate

var req = HttpWebRequest.Create(url) as HttpWebRequest;

req.ServicePoint.BindIPEndPointDelegate = (servicePoint, remoteEndPoint, retryCount) =>
{
    if (remoteEndPoint.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
    {
        return new IPEndPoint(IPAddress.IPv6Any, 0);
    }

    throw new InvalidOperationException("no IPv6 address");
};

5
投票

几年后,.NET 5 的答案:

假设您使用的是

HttpClient
,那么您可以在
ConnectCallback
上设置
SocketsHttpHandler
,从而:

private static readonly HttpClient _http = new HttpClient(new SocketsHttpHandler() {
    ConnectCallback = async (context, cancellationToken) => {
        // Use DNS to look up the IP address(es) of the target host
        IPHostEntry ipHostEntry = await Dns.GetHostEntryAsync(context.DnsEndPoint.Host);

        // Filter for IPv4 addresses only
        IPAddress ipAddress = ipHostEntry
            .AddressList
            .FirstOrDefault(i => i.AddressFamily == AddressFamily.InterNetwork);

        // Fail the connection if there aren't any IPV4 addresses
        if (ipAddress == null) {
            throw new Exception($"No IP4 address for {context.DnsEndPoint.Host}");
        }

        // Open the connection to the target host/port
        TcpClient tcp = new();
        await tcp.ConnectAsync(ipAddress, context.DnsEndPoint.Port, cancellationToken);

        // Return the NetworkStream to the caller
        return tcp.GetStream();
    }),
});

(此设置仅适用于 IPv4,若要仅设置 IPv6,请将 AddressFamiliy.InterNetwork 更改为 AddressFamily.InterNetworkV6)


2
投票

这是我基于 @Moose Morals 的解决方案,对缓存 IP 进行了一些优化,这几乎没有什么增益(在我的专业 PC 上为 1 毫秒,但在你的 PC 上可能会更高)

(仅当主机改变时才搜索IP)

我缩短了一点,但效果是一样的

public class ResolveDnsOptimization
{
    public static void ApplyTo(SocketsHttpHandler handler)
    {
        CachedAddress cachedAddress = null;

        // Remove the latencies when using host name over IP address
        // Changing pool connection lifetime and forcing to open them all does not work, the DNS resolution is always done.
        // Source: https://stackoverflow.com/a/70475741/1529139
        handler.ConnectCallback = async (context, cancellationToken) =>
            {
                if (cachedAddress == null || cachedAddress.Host != context.DnsEndPoint.Host)
                {
                    // Use DNS to look up the IP address(es) of the target host and filter for IPv4 addresses only
                    IPHostEntry ipHostEntry = await Dns.GetHostEntryAsync(context.DnsEndPoint.Host);
                    IPAddress ipAddress = ipHostEntry.AddressList.FirstOrDefault(i => i.AddressFamily == AddressFamily.InterNetwork);
                    if (ipAddress == null)
                    {
                        cachedAddress = null;
                        throw new Exception($"No IP4 address for {context.DnsEndPoint.Host}");
                    }
                    cachedAddress = new CachedAddress() { Ip = ipAddress, Host = context.DnsEndPoint.Host };
                }

                TcpClient tcp = new();
                await tcp.ConnectAsync(cachedAddress.Ip, context.DnsEndPoint.Port, cancellationToken);
                return tcp.GetStream();
            };
    }

    private class CachedAddress
    {
        public IPAddress Ip;
        public string Host;
    }
}

用途:

SocketsHttpHandler handler = new SocketsHttpHandler();
ResolveDnsOptimization.ApplyTo(handler); // <- here
HttpClient client = new HttpClient(handler)

0
投票

上述两种选择

  1. 关闭应用程序内的 IPv6 解析,请参阅 Dotnet Core Runtime Config 了解说明。
  2. 使用
    Dns.GetHostAddresses(hostname, System.Net.Sockets.AddressFamily.InterNetwork)
    返回 IPv4 地址数组(对 IPv6 使用 InterNetwork6)并使用 url 中的其中一个地址。

选项 1 确实有效(可能需要重新加载或重建应用程序才能使其正常工作...检查 .in\dubug.runtimeconfig.json 文件进行确认。)我有一个使用 Ipv6 的 httpclient,但网络服务仅侦听 Ipv4。进行更改后,httpclient 只能看到 ipv4 地址。

我在httpclient本身没有发现任何东西;因此,如果您只想影响特定的 httpclient 调用,那么其他答案更合适。

© www.soinside.com 2019 - 2024. All rights reserved.