如何在C#中强制使用IPv6或IPv4进行HttpWebRequest或WebRequest?

5

我来自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) 中实现相同的功能?

我能想到一种方法,就是自己通过DNS请求A或AAAA记录,进行直接IP请求并设置 host: 头部。是否有更好的方法?

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`,那么可以在 `SocketsHttpHandler` 上设置 `ConnectCallback`,例如:
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,请将AddressFamily.InterNetwork更改为AddressFamily.InterNetworkV6。


2
谢谢!这解决了我的问题。有时使用“localhost”而不是“127.0.0.1”会导致2秒的延迟。此外,请参见此处的解决方案 https://dev59.com/6sTra4cB1Zd3GeqPugX4#71641927 - 56ka

2
这是基于@Moose Morals的解决方案,我进行了一些优化,缓存IP地址,这只会带来微小的收益(在我的专业PC上为1ms,但在您的电脑上可能更高)。
(仅在主机更改时搜索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://dev59.com/g5ffa4cB1Zd3GeqP6FBE#70475741
        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地址数组(使用InterNetwork6代替IPv6),然后在URL中使用其中一个地址。

选项1确实有效(可能需要重新加载或重建应用程序才能使其生效...请检查.\bin\dubug<app>.runtimeconfig.json文件以确认)。我有一个使用IPv6的httpclient,但Web服务只监听IPv4。在进行更改后,httpclient只能看到IPv4地址。

我在httpclient本身中没有找到任何解决方案;因此,如果您只想影响特定的httpclient调用,其他答案更为合适。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接