c# - 使用 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 也没有受到影响,并且相同的代理可以通过它正确发送请求。
解决方案
我对 HttpClient 也有同样的问题。要返回 SendAsync 需要做两件事:首先,设置发生通信的 TCP 通道(SYN、SYN/ACK、ACK 握手,如果您熟悉的话),其次取回构成通过该 TCP 通道的 HTTP 响应。HttpClient 的超时仅适用于第二部分。第一部分的超时由操作系统的网络子系统控制,在 .NET 代码中更改该超时非常困难。
(这里是重现这种效果的方法。在两台机器之间建立一个有效的客户端/服务器连接,这样你就知道名称解析、端口访问、侦听以及客户端和服务器逻辑都可以工作。然后拔下服务器上的网线,然后重新运行客户端请求。无论您在 HttpClient 上设置什么超时,它都会以操作系统的默认网络超时超时。)
我知道的唯一方法是在不同的线程上启动您自己的延迟计时器,如果计时器首先完成,则取消 SendAsync 任务。您可以使用 Task.Delay 和 Task.WaitAny 或通过使用您想要的时间创建一个 CancellationTokenSource 来执行此操作(这实际上只是在引擎盖下执行第一种方式)。在任何一种情况下,您都需要小心取消和读取输掉比赛的任务的异常。
推荐阅读
- sql - 使用 JOIN 显示 MAX 记录
- amazon-web-services - 多区域访问 AWS CLI
- c# - 在 ASP.NET Core Web API 中为生产环境设置 launchurl
- django - 我如何将 django url 参数传递给重定向功能
- regex - 过滤 4 个大写字母
- flutter - graphql_flutter - 从 pub.dev 页面复制代码 - 仍然出现错误
- javascript - 未捕获的 SyntaxError:标识符“myOptions”已在 JavaScript 中声明
- pine-script - 每次 MACD 研究中的直方图会出现更高的低点时发出警报,反之亦然
- apache-kafka - 如何在 kafka 消费者中消费和解析不同的 Avro 消息
- c++ - 这是在函数中实现 return 语句的正确方法吗?我的函数不会返回值