2010-01-15 69 views
3

我正在创建一个小型测试应用程序,它将运行几个测试用例来检查我们系统各个组件的状态。该应用程序创建为.NET Windows窗体应用程序,并带有几个复选框,用于检查每个通过的测试用例(如果失败则不检查)。如何使用异步调用来检查Web服务是否可用

其中一项测试是检查简单web服务的可用性。如果Web服务可用,则测试用例将成功,否则失败。我首先尝试通过使用System.Net.HttpWebRequest类来完成此任务,将超时设置为1500毫秒并调用GetResponse。现在,如果web服务不可用,请求应该超时并且测试用例失败。然而,事实证明,超时是在web服务的实际响应上设置的。也就是说,超时从开始建立web服务的连接时开始计算。如果Web服务无法访问,则不会发生超时,因此在测试用例失败之前至少需要30秒。

为了解决这个问题,我建议使用BeginGetResponse和请求完成时的回调进行异步调用。现在,这里也会出现同样的问题,因为如果web服务不可用,则至少需要30秒,直到发生回调。

现在我在想我可以使用System.Timers.Timer,将超时设置为1500毫秒,并在计时器超时时发生事件。问题是,定时器会超时,无论Web服务是否可访问,因此事件将被触发,而不管Web服务是否可访问。

现在我没有想法。我意识到这必须是一个相当容易解决的问题。有没有人有任何建议,我怎么能做到这一点?

我收到了在自己的线程中进行调用的建议。所以现在我在自己的线程中执行请求。但是,如果请求没有响应,则当我试图将测试状态设置为失败时,应用程序会锁定。但是如果它有回应,我可以设置测试状态以成功解决任何问题。代码为:

private void TestWebServiceConnection() 
     { 
      HttpWebRequest request; 
      request = (HttpWebRequest)WebRequest.Create(Url); 
      request.BeginGetResponse(new AsyncCallback(FinishWebRequest), null); 
      System.Threading.Thread.Sleep(1500); 
      SetStatusDelegate d = new SetStatusDelegate(SetTestStatus); 
      if (request.HaveResponse) 
      { 
       if (ConnectionTestCase.InvokeRequired) 
       { 
        this.Invoke(d, new object[] { TestStatus.Success}); 
       } 
      } 
      else 
      { 
       if (ConnectionTestCase.InvokeRequired) 
       { 
        this.Invoke(d, new object[] { TestStatus.Failure }); 
       } 
      } 

     } 
     private delegate void SetStatusDelegate(TestStatus t); 

     private void SetTestStatus(TestStatus t) 
     { 
      ConnectionTestCase.Status = t; 
     } 

ThreadStart ts = new ThreadStart(TestWebServiceConnection); 
Thread t = new Thread(ts); 
t.Start(); 

任何意见,为什么我得到这种行为?感谢名单!

回答

2

嗯..真奇怪的设置超时的方法。你为什么不使用HttpWebRequest对象的timeout属性?然后用try..catch(TimeoutException)环绕HttpWebRequest.GetResponse()。它不应该是异步的。

这个超时准确确定了确定工作连接所需的时间。如果你以后想要控制最大的GetResponse()时间,你应该使用MemoryStream来读取响应流,它也有readtimeout属性。而已。

如果您仍然希望它在单独的线程中运行这样做:

Thread testThread = new Thread(() => { 
    SetStatusDelegate d = new SetStatusDelegate(SetTestStatus); 
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create("yoururl"); 
    request.Timeout = new TimeSpan(0, 1, 30); 
    try 
    { 
     request.GetResponse(); 
    } 
    catch(TimeoutException) 
    { 
     this.Invoke(d, new object[] { TestStatus.Failure}); 
    } 
    this.Invoke(d, new object[] { TestStatus.Success}); 
}); 

或者类似的东西:)

+0

我跟着你的意见,并在一个单独的线程上打电话。在我调用thread.start()之后,我让应用程序休眠了一秒钟。如果未收到响应,则测试默认为失败。感谢您的帮助! – 2010-02-04 07:25:33

0

你为什么不在新的线程中自己进行调用? “

+0

我试图按照你的意见。所以我把这个调用放在了自己的线程中。现在,当我尝试以表单的形式设置复选框的状态时,我收到异常(跨线程调用)。为了解决这个问题,我试图让它线程安全。但是,如果请求没有响应,当我尝试将测试状态设置为失败时,应用程序会锁定。我在问题中发布了上述代码。 – 2010-01-15 10:17:44

0

”计时器将超时,无论Web服务是否可访问,因此该事件将独立于Web服务是否可访问而被触发。“

一旦Web服务可以访问,您就可以停止计时器。 如果定时器调用设置为2000ms,并且Web服务调用的完成时间少于2000ms,则禁用该定时器,否则定时器事件将通知Web服务出现问题

0

您也可以考虑使用BackgroundWorker对象以更线程安全的方式执行这些调用。它有能力执行异步服务并报告回父表单/对象。然后,您可以将任何时间长度和规则附加到您认为必要的Web请求上,而不必担心线程异常。

0

我试图解决您的问题,但我没有完成它。也许别人可以修复它的错误:

using System; 
using System.ComponentModel; 
using System.Drawing; 
using System.Windows.Forms; 
using System.Net; 
using System.Threading; 

namespace winformapp 
{ 
    public partial class Form1 : Form 
    { 
     private bool completedInTime = false; 
     private HttpWebRequest request = null; 
     public delegate void RequestCompletedDelegate(bool completedSuccessfully); 
     private static AutoResetEvent resetEvent = new AutoResetEvent(false); 
     private System.Threading.Timer timer = null; 
     private static readonly object lockOperation = new object(); 
     public Form1() 
     { 
      InitializeComponent(); 
      urlTextBox.Text = "http://twitter.com"; 
      millisecondsTextBox.Text = "10000"; 
     } 

     private void handleUIUpdateRequest(bool completedSuccessfully) 
     { 
      resultCheckbox.Checked = completedSuccessfully; 
      startTestButton.Enabled = true; 
      completedInTime = false; 
     } 

     private void button1_Click(object sender, EventArgs e) 
     { 
      startTestButton.Enabled = false; 
      try 
      { 
       TestWebServiceConnection(urlTextBox.Text, 
        Convert.ToInt32(millisecondsTextBox.Text)); 
       resetEvent.WaitOne(); 
       resultCheckbox.BeginInvoke(new RequestCompletedDelegate(handleUIUpdateRequest), completedInTime); 
       timer.Change(Timeout.Infinite, Timeout.Infinite); 
      } 
      catch (Exception ex) 
      { 

      } 
     } 
     private void TestWebServiceConnection(string url, int timeoutMilliseconds) 
     { 
      request = (HttpWebRequest)WebRequest.Create(url); 
      request.BeginGetResponse(new AsyncCallback(FinishWebRequest), null); 
      timer = new System.Threading.Timer(new TimerCallback(abortRequest), null, timeoutMilliseconds, Timeout.Infinite); 
     } 
     private void FinishWebRequest(IAsyncResult iar) 
     { 
       lock (lockOperation) 
       { 
        completedInTime = true; 
        resetEvent.Set(); 
       } 

       /* 
       if (timer != null) 
       { 
        timer.Dispose(); 
        try 
        { 
         if (request != null) 
         { 
          request.Abort(); 
         } 
        } 
        catch { } 
       } 
       timer = null; 
       request = null; 

      } 
       * */ 
     } 
     private void abortRequest(object state) 
     { 
      lock (lockOperation) 
      { 
       resetEvent.Set(); 
      } 
      try 
      { 
       Thread.CurrentThread.Abort(); 
      } 
      catch {} 
      /* 
      lock (lockOperation) 
      { 
       if (!completedInTime) 
       { 
        resetEvent.Set(); 
       } 
      } 

      if (completedInTime == false) 
      { 
       lock (lockOperation) 
       { 
        completedInTime = false; 
       } 
       try 
       { 
        if (request != null) 
        { 
         request.Abort(); 
        } 
       } 
       catch { } 
       try 
       { 
        if (timer != null) 
        { 
         timer.Dispose(); 
        } 
       } 
       catch { } 
       finally 
       { 
        resetEvent.Set(); 
       } 

       request = null; 
       timer = null; 

      } 
      * */ 
     } 
    } 
}