C# HttpClient的cURL请求-如何设置服务器连接(WinInet)的超时

C# HttpClient的cURL请求-如何设置服务器连接(WinInet)的超时,c#,curl,timeout,dotnet-httpclient,C#,Curl,Timeout,Dotnet Httpclient,我使用HttpClient通过下面描述的方法发送cURL请求 此方法使用的参数为: SelectedProxy=存储代理参数的自定义类 Parameters.WcTimeout=超时 url,header,content=cURL请求(基于此工具转换为C#) 如果我在没有代理的情况下运行它,它就像一个符咒。 当我使用我首先从Chrome测试的代理发送请求时,我在try{}catch{}上有以下错误。这是错误树 {"An error occurred while sending the reque

我使用HttpClient通过下面描述的方法发送cURL请求

此方法使用的参数为:

SelectedProxy=存储代理参数的自定义类

Parameters.WcTimeout=超时

url,header,content=cURL请求(基于此工具转换为C#)

如果我在没有代理的情况下运行它,它就像一个符咒。 当我使用我首先从Chrome测试的代理发送请求时,我在try{}catch{}上有以下错误。这是错误树

{"An error occurred while sending the request."}
    InnerException {"Unable to connect to the remote server"}
        InnerException {"A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond [ProxyAdress]"}
        SocketErrorCode: TimedOut
通过使用秒表,我看到时间是在大约30秒后出现的


我根据以下链接或WinHttpHandler尝试了几种不同的处理程序

值得注意的是,WinHttpHandler允许使用不同的错误代码,即错误12002调用WINHTTP\u回调\u状态\u请求\u错误“操作超时”。根本原因是相同的,尽管它有助于找到它的bug(即WinInet),这也证实了@DavidWright所说的关于HttpClient超时管理请求发送的不同部分的说法

因此,我的问题来自与服务器建立连接所需的时间,这会触发WinInet的30秒超时

我的问题是如何更改这些超时?


另一方面,值得注意的是,使用WinInet的Chrome似乎没有出现此超时问题,我的应用程序的很大一部分都基于Cefsharp,通过它,相同的代理可以正确发送请求。

我在HttpClient上也遇到过同样的问题。要返回SendAsync,需要做两件事:首先,设置通信发生的TCP通道(SYN、SYN/ACK、ACK握手,如果您熟悉的话),然后通过该TCP通道返回构成HTTP响应的数据。HttpClient的超时仅适用于第二部分。第一部分的超时由操作系统的网络子系统控制,在.NET代码中很难更改该超时

(以下是如何重现这种效果。在两台计算机之间设置一个工作的客户端/服务器连接,这样您就知道名称解析、端口访问、侦听以及客户端和服务器逻辑都可以工作。然后拔下服务器上的网络电缆并重新运行客户端请求。无论发生什么情况,它都会随着操作系统的默认网络超时而超时您在HttpClient上设置的超时。)


我知道的唯一解决方法是在不同的线程上启动自己的延迟计时器,如果计时器首先完成,则取消SendAsync任务。可以使用task.delay和task.WaitAny或使用所需的timeone创建CancellationTokenSource(这基本上只是第一种方式)。在任何一种情况下,您都需要小心取消和读取失去竞争的任务中的异常。

我在HttpClient上也遇到了同样的问题。要返回SendAsync,需要做两件事:首先,设置发生通信的TCP通道(SYN、SYN/ACK、ACK握手,如果您熟悉的话)和第二次通过该TCP通道返回构成HTTP响应的数据。HttpClient的超时仅适用于第二部分。第一部分的超时由操作系统的网络子系统控制,在.NET代码中更改该超时非常困难

(以下是如何重现这种效果。在两台计算机之间设置一个工作的客户端/服务器连接,这样您就知道名称解析、端口访问、侦听以及客户端和服务器逻辑都可以工作。然后拔下服务器上的网络电缆并重新运行客户端请求。无论发生什么情况,它都会随着操作系统的默认网络超时而超时您在HttpClient上设置的超时。)


我知道的唯一解决方法是在不同的线程上启动自己的延迟计时器,如果计时器首先完成,则取消SendAsync任务。可以使用task.delay和task.WaitAny或使用所需的timeone创建CancellationTokenSource(这基本上只是第一种方式)。在任何一种情况下,您都需要小心取消并读取失败任务的异常。

因此感谢@DavidWright,我了解了一些事情:

  • 在发送
    HttpRequestMessage
    并从
    HttpClient
    开始超时之前,启动到服务器的TCP连接
  • TCP连接有自己的超时,在操作系统级别定义,我们没有确定在运行时从C#更改它的方法(如果有人想参与,问题待定)
  • 坚持尝试连接是可行的,因为每次尝试都会从以前的尝试中获益,不过需要实现适当的异常管理和手动超时计数器(我实际上在代码中考虑了多次尝试,假设每次尝试大约30秒)
  • 所有这些加在一起形成了以下代码:

            const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
            const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;
            ServicePointManager.SecurityProtocol = Tls12;
            var sp = ServicePointManager.FindServicePoint(endpoint);
    
            sp.ConnectionLeaseTimeout = (int)Parameters.ConnectionLeaseTimeout.TotalMilliseconds;
    
    
            string source = "";
    
            using (var handler = new HttpClientHandler())
            {
                handler.UseCookies = usecookies;
                WebProxy wp = new WebProxy(SelectedProxy.Address);
                handler.Proxy = wp;
    
                using (var client = new HttpClient(handler))
                {
                    client.Timeout = Parameters.WcTimeout;
    
                    int n = 0;
                    back:
                    using (var request = new HttpRequestMessage(new HttpMethod(HttpMethod), endpoint))
                    {
    
                        if (headers != null)
                        {
                            foreach (var h in headers)
                            {
                                request.Headers.TryAddWithoutValidation(h.Item1, h.Item2);
                            }
                        }
                        if (content != "")
                        {
                            request.Content = new StringContent(content, Encoding.UTF8, "application/x-www-form-urlencoded");
                        }
                        HttpResponseMessage response = new HttpResponseMessage();
    
                        try
                        {
                            response = await client.SendAsync(request);
                        }
                        catch (Exception e)
                        {
                            if(e.InnerException != null)
                            {
                                if(e.InnerException.InnerException != null)
                                {
                                    if (e.InnerException.InnerException.Message.Contains("A connection attempt failed because the connected party did not properly respond after"))
                                    {
                                        if (n <= Parameters.TCPMaxTries)
                                        {
                                            n++;
                                            goto back;
                                        }
                                    }
                                }
                            }
                            // Manage here other exceptions
                        }
                        source = await response.Content.ReadAsStringAsync();
                    }
                }
            }
            return source;
    
    const SslProtocols\u Tls12=(SslProtocols)0x00000C000;
    const SecurityProtocolType Tls12=(SecurityProtocolType)\u Tls12;
    ServicePointManager.SecurityProtocol=Tls12;
    var sp=ServicePointManager.FindServicePoint(端点);
    sp.ConnectionLeaseTimeout=(int)Parameters.ConnectionLeaseTimeout.totalMillicons;
    字符串源=”;
    使用(var handler=new-HttpClientHandler())
    {
    handler.UseCookies=UseCookies;
    WebProxy wp=新的WebProxy(SelectedProxy.Address);
    handler.Proxy=wp;
    使用(var客户端=新的HttpClient(处理程序))
    {
    client.Timeout=Parameters.WcTimeout;
    int n=0;
    背面:
    使用(var请求=新的HttpRequestMessage(新的HttpMethod(HttpMethod),端点))
    {
    
            const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
            const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;
            ServicePointManager.SecurityProtocol = Tls12;
            var sp = ServicePointManager.FindServicePoint(endpoint);
    
            sp.ConnectionLeaseTimeout = (int)Parameters.ConnectionLeaseTimeout.TotalMilliseconds;
    
    
            string source = "";
    
            using (var handler = new HttpClientHandler())
            {
                handler.UseCookies = usecookies;
                WebProxy wp = new WebProxy(SelectedProxy.Address);
                handler.Proxy = wp;
    
                using (var client = new HttpClient(handler))
                {
                    client.Timeout = Parameters.WcTimeout;
    
                    int n = 0;
                    back:
                    using (var request = new HttpRequestMessage(new HttpMethod(HttpMethod), endpoint))
                    {
    
                        if (headers != null)
                        {
                            foreach (var h in headers)
                            {
                                request.Headers.TryAddWithoutValidation(h.Item1, h.Item2);
                            }
                        }
                        if (content != "")
                        {
                            request.Content = new StringContent(content, Encoding.UTF8, "application/x-www-form-urlencoded");
                        }
                        HttpResponseMessage response = new HttpResponseMessage();
    
                        try
                        {
                            response = await client.SendAsync(request);
                        }
                        catch (Exception e)
                        {
                            if(e.InnerException != null)
                            {
                                if(e.InnerException.InnerException != null)
                                {
                                    if (e.InnerException.InnerException.Message.Contains("A connection attempt failed because the connected party did not properly respond after"))
                                    {
                                        if (n <= Parameters.TCPMaxTries)
                                        {
                                            n++;
                                            goto back;
                                        }
                                    }
                                }
                            }
                            // Manage here other exceptions
                        }
                        source = await response.Content.ReadAsStringAsync();
                    }
                }
            }
            return source;