.Net WebRequest超时与TCP超时

时间:2015-03-28 10:42:16

标签: c# .net tcp

问题

如何为单个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

2 个答案:

答案 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.");
        }
    }
}