首页 > 解决方案 > 使用 HttpClient 的 cURL 请求 - 如何在服务器连接上设置超时 (WinInet)

问题描述

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

此方法使用的参数是:

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

Parameters.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 秒后。


我根据以下链接尝试了一些不同的处理程序HttpClient.Timeout 和使用 WebRequestHandler 超时属性有什么区别?, HttpClient Timeout 混淆或与 WinHttpHandler 混淆。

值得注意的是,WinHttpHandler 允许使用不同的错误代码,即 Error 12002 calls WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, 'The operation timed out'。根本原因是相同的,尽管它有助于定位它的错误位置(即 WinInet),这也证实了@DavidWright 关于 HttpClient 的超时管理请求发送的不同部分的说法。

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

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

在旁注中,值得注意的是,使用 WinInet 的 Chrome 似乎并没有受到这种超时的影响,我的应用程序的很大一部分所基于的 Cefsharp 也没有受到影响,并且相同的代理可以通过它正确发送请求。

标签: c#curltimeoutdotnet-httpclient

解决方案


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

(这里是重现这种效果的方法。在两台机器之间建立一个有效的客户端/服务器连接,这样你就知道名称解析、端口访问、侦听以及客户端和服务器逻辑都可以工作。然后拔下服务器上的网线,然后重新运行客户端请求。无论您在 HttpClient 上设置什么超时,它都会以操作系统的默认网络超时超时。)

我知道的唯一方法是在不同的线程上启动您自己的延迟计时器,如果计时器首先完成,则取消 SendAsync 任务。您可以使用 Task.Delay 和 Task.WaitAny 或通过使用您想要的时间创建一个 CancellationTokenSource 来执行此操作(这实际上只是在引擎盖下执行第一种方式)。在任何一种情况下,您都需要小心取消和读取输掉比赛的任务的异常。


推荐阅读