简体   繁体   English

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

[英]How to force ipv6 or ipv4 for HttpWebRequest or WebRequest C#

Coming from node.js I can do this to tell node.js to make the request using ipv6 vs ipv4来自 node.js 我可以这样做来告诉 node.js 使用 ipv6 vs 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();

Setting family to 4 forces ipv4, setting it to 6 forces ipv6.family设置为4强制 ipv4,将其设置为6强制 ipv6。 Not setting it lets either work.不设置它可以让任何一个工作。

How can I do the same thing in C# (.NET 3.5)如何在 C# (.NET 3.5) 中做同样的事情

I can think of one way which is to make a DNS request myself myself for the A or AAAA records, make a direct IP request and set the host: header.我可以想到一种方法,即自己为 A 或 AAAA 记录发出 DNS 请求,发出直接 IP 请求并设置host:标头。 Is there a better way?有没有更好的办法?

You can use ServicePoint.BindIPEndPointDelegate .您可以使用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");
};

A few years later, a .NET 5 answer:几年后,一个 .NET 5 的答案:

Assuming you're using a HttpClient , then you can set a ConnectCallback on the SocketsHttpHandler thus:假设您使用的是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();
    }),
});

(This is setup for IPv4 only, to set for Ipv6 only change AddressFamiliy.InterNetwork to AddressFamily.InterNetworkV6) (这仅针对 IPv4 设置,仅针对 Ipv6 设置将 AddressFamily.InterNetwork 更改为 AddressFamily.InterNetworkV6)

Here is my solution based on @Moose Morals, with a little optimization on caching the IP which makes a really little gain (1ms on my professional PC but it might be higher on yours)这是我基于@Moose Morals 的解决方案,对缓存 IP 进行了一些优化,这确实带来了一点收益(在我的专业 PC 上为 1ms,但在您的 PC 上可能更高)

(search IP only when host changed) (仅在主机更改时搜索 IP)

I shortened a little bit but it works the same我缩短了一点,但它的工作原理是一样的

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;
    }
}

Usage :用法 :

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM