如何为单个WebRequest配置TCP超时?
根据文档WebRequest.Timeout:
请求超时之前的时间长度(以毫秒为单位),或 值Timeout.Infinite表示请求没有时间 出。默认值由后代类定义。
从不存在的端点(在请求的端口上没有服务绑定的IIS)请求Web资源失败,并且具有大约 21秒的不同且常量的超时。
根据此serverfault answer,这似乎是连接TCP超时。
public static async Task<string> WebRequestToString(string requestUri, int timeoutMilliseconds)
{
var request = WebRequest.Create(requestUri) as HttpWebRequest;
request.KeepAlive = false;
request.Timeout = timeoutMilliseconds;
request.ReadWriteTimeout = timeoutMilliseconds;
using (var response = await request.GetResponseAsync() as HttpWebResponse)
{
// Get the response stream
using (var reader = new StreamReader(response.GetResponseStream()))
{
var responseBody = await reader.ReadToEndAsync();
return responseBody;
}
}
}
static void Main(string[] args)
{
string uri = "http://10.15.1.24:8081/thiservicedoesnotexist/";
int timeoutMilliseconds = 10;
Stopwatch sw = new Stopwatch();
sw.Start();
try
{
WebRequestToString(uri, timeoutMilliseconds).Wait();
}
catch (AggregateException ex)
{
Console.WriteLine(ex.ToString());
}
sw.Stop();
Console.WriteLine("Elaped {0}ms", sw.ElapsedMilliseconds);
Console.ReadKey();
}
System.AggregateException: One or more errors occurred. ---> System.Net.WebExcep
tion: Unable to connect to the remote server ---> System.Net.Sockets.SocketExcep
tion: A connection attempt failed because the connected party did not properly r
espond after a period of time, or established connection failed because connecte
d host has failed to respond 10.15.1.24:8081
at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Sock
et s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state,
IAsyncResult asyncResult, Exception& exception)
--- End of inner exception stack trace ---
at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar,
Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchron
ization)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
ification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at TimeoutTests.Program.<WebRequestToString>d__0.MoveNext() in \\vmware-host\
shared folders\Documents\Visual Studio 2012\Projects\TimeoutTests\TimeoutTests\P
rogram.cs:line 20
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceled
Exceptions)
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationTo
ken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at TimeoutTests.Program.Main(String[] args) in \\vmware-host\shared folders\D
ocuments\Visual Studio 2012\Projects\TimeoutTests\TimeoutTests\Program.cs:line 4
0
---> (Inner Exception #0) System.Net.WebException: Unable to connect to the remo
te server ---> System.Net.Sockets.SocketException: A connection attempt failed b
ecause the connected party did not properly respond after a period of time, or e
stablished connection failed because connected host has failed to respond 10.15.
1.24:8081
at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Sock
et s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state,
IAsyncResult asyncResult, Exception& exception)
--- End of inner exception stack trace ---
at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar,
Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchron
ization)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
ification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at TimeoutTests.Program.<WebRequestToString>d__0.MoveNext() in \\vmware-host\
shared folders\Documents\Visual Studio 2012\Projects\TimeoutTests\TimeoutTests\P
rogram.cs:line 20<---
Elaped 21169ms
答案 0 :(得分:2)
我非常怀疑这个超时实际上与TCP连接超时相同。 webrequest超时的文档如下:
The length of time, in milliseconds, until the request times out, or the value Timeout.Infinite to indicate that the request does not time out. The default value is defined by the descendant class.
这是您的请求级别的超时,即HTTP。可能是建立了与服务器的连接,但创建响应需要很长时间。也是时间可以超时。
webrequest如何处理此问题并将其映射到TCP上是依赖于实现的。 连接建立的TCP超时不能是无限的。
我还阅读了以下内容:
The Timeout property indicates the length of time, in milliseconds, until the request times out and throws a WebException. The Timeout property affects only synchronous requests made with the GetResponse method. To time out asynchronous requests, use the Abort method.
你是异步的!
答案 1 :(得分:1)
我最终通过创建基于approach的GetResponseAsync方法扩展来修复此问题,并且似乎工作正常:
public static class WebRequestExtensions
{
public static async Task<WebResponse> GetResponseAsyncWithTimeout(this WebRequest request)
{
var timeoutCancellationTokenSource = new CancellationTokenSource();
var responseTask = request.GetResponseAsync();
var completedTask = await Task.WhenAny(responseTask, Task.Delay(request.Timeout, timeoutCancellationTokenSource.Token));
if (completedTask == responseTask)
{
timeoutCancellationTokenSource.Cancel();
return await responseTask;
}
else
{
request.Abort();
throw new TimeoutException("The operation has timed out.");
}
}
}