使用HttpClient的cURL请求-如何在服务器连接上设置超时(WinInet)

s1ag04yj  于 2022-11-13  发布在  其他
关注(0)|答案(2)|浏览(284)

我使用HttpClient通过下面描述的方法发送cURL请求。
用于此方法的参数为:
SelectedProxy =存储代理参数的自定义类
参数.WcTimeout =超时时间
url,header,content =请求的cURL(基于此工具转换为C#https://curl.olsh.me/)。

const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
        const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;
        ServicePointManager.SecurityProtocol = Tls12;
        string source = "";

        using (var handler = new HttpClientHandler())
        {
            handler.UseCookies = usecookies;
            WebProxy wp = new WebProxy(SelectedProxy.Address);
            handler.Proxy = wp;

            using (var httpClient = new HttpClient(handler))
            {
                httpClient.Timeout = Parameters.WcTimeout;

                using (var request = new HttpRequestMessage(new HttpMethod(HttpMethod), url))
                {
                    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 httpClient.SendAsync(request);
                    }
                    catch (Exception e)
                    {
                        //Here the exception happens
                    }
                    source = await response.Content.ReadAsStringAsync();
                }
            }
        }
        return source;

如果我运行这个没有代理,它的工作就像一个魅力。当我发送一个请求使用代理,我测试了第一次从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

通过使用秒表,我看到TimedOut发生在大约30秒后。
我尝试了几个不同的处理程序,基于以下链接What's the difference between HttpClient.Timeout and using the WebRequestHandler timeout properties?HttpClient Timeout confusion或使用WinHttpHandler。
值得注意的是,WinHttpHandler允许使用不同的错误代码,即调用WINHTTP_CALLBACK_STATUS_REQUEST_ERROR的错误12002,"操作超时“。虽然它有助于定位其错误所在(即WinInet),但基本原因是相同的,这也证实了@DavidWright所说的HttpClient的超时管理请求发送的不同部分。
因此,我的问题来自于建立到服务器的连接所需的时间,这会触发WinInet的30秒超时。

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

顺便说一句,值得注意的是,使用WinInet的Chrome似乎没有受到这种超时的影响,我的应用程序的很大一部分都基于Cefsharp,通过它,相同的代理可以正确地发送请求。

jbose2ul

jbose2ul1#

所以感谢@DavidWright,我明白了一些事情:
1.在发送HttpRequestMessageHttpClient超时开始之前,将启动到服务器的TCP连接

  1. TCP连接有其自己的超时,在操作系统级别定义,我们没有确定在C#运行时更改它的方法(如果有人想提供帮助,问题待定)
    1.坚持尝试连接工作,因为每次尝试都受益于以前的尝试,尽管需要实现适当的异常管理和手动超时计数器(我实际上在我的代码中考虑了多次尝试,假设每次尝试大约为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;

顺便说一句,我目前对HttpClient的实现在将来可能会有问题。虽然HttpClient是一次性的,但它应该在应用程序级别通过一个static来定义,而不是在using语句中定义。要了解更多信息,请访问herethere
我的问题是,我希望在每次请求时更新代理,而不是在每次请求的基础上设置代理。

r8uurelv

r8uurelv2#

我在使用HttpClient时也遇到过同样的问题。SendAsync返回需要发生两件事:首先,设置发生通信的TCP通道(SYN、SYN/ACK、ACK握手,如果您熟悉的话),然后通过该TCP通道获取构成HTTP响应的数据。HttpClient的超时仅适用于第二部分。第一部分的超时由操作系统的网络子系统控制,并且在.NET代码中很难更改该超时。
(Here这是您可以重现这种效果的方法。在两台计算机之间建立一个有效的客户端/服务器连接,这样您就知道名称解析、端口访问、侦听以及客户端和服务器逻辑都可以正常工作。然后拔下服务器上的网络电缆,重新运行客户端请求。无论您在HttpClient上设置了什么超时,它都会超时,并使用操作系统的默认网络超时。)
我所知道的解决这个问题的唯一方法是在另一个线程上启动您自己的延迟计时器,如果计时器先完成,则取消SendAsync任务。(基本上只是在引擎盖下做第一种方式)。在这两种情况下,您都需要小心取消和阅读竞争失败的任务中的异常。

相关问题