2010-11-19 32 views
0

我們在EWS手冊(以下代碼)中基於HTTPListener創建了Exchange Web服務推送通知偵聽器。它在運行單核Xeon的Win 2008服務器上運行得很好。現在我們將它移動到帶有四核opteron CPU的Win 2008 R2服務器,並且在Listener使用HTTPListenerExceptions初始化後立即崩潰。現在因爲沒有什麼改變,但我認爲這可能與多線程有關。也許有人可以建議,謝謝。HTTPListener在四核機器上崩潰Win 2008 Server R2

public class PushNotificationClient 
{ 
    private uint portNumber; 
    private NotificationEventsReceived eventHandler; 
    private bool isListening = false; 
    private ManualResetEvent stopEvent = new ManualResetEvent(false); 
    private bool shouldStop = false; 
    private XmlNamespaceManager mgr; 
    private XmlSerializer ser; 

    /// <summary> 
    /// Constructor 
    /// </summary> 
    /// <param name="portNumber">Port number to listen on</param> 
    /// <param name="eventHandler">delegate to call when notifications are 
    /// received</param> 
    /// 
    public PushNotificationClient(
    uint portNumber, 
    NotificationEventsReceived eventHandler) 
    { 
     this.portNumber = portNumber; 
     if (eventHandler == null) 
     { 
      throw new ArgumentNullException("eventHandler"); 
     } 
     this.eventHandler = eventHandler; 
     // namespace manager is used for XPath queries when parsing the request 
     // 
     this.mgr = new XmlNamespaceManager(new NameTable()); 
     this.mgr.AddNamespace("t", 
     "http://schemas.microsoft.com/exchange/services/2006/types"); 
     this.mgr.AddNamespace("m", 
     "http://schemas.microsoft.com/exchange/services/2006/messages"); 
     // XmlSerializer is used to convert SendNotification elements into proxy 
     // class instances 
     // 
     this.ser = new XmlSerializer(typeof(SendNotificationResponseType)); 
    } 

    /// <summary> 
    /// Start Listening 
    /// </summary> 
    public void StartListening() 
    { 
     VerifyNotListening(); 
     this.stopEvent.Reset(); 
     this.shouldStop = false; 
     // Run the listener on a background thread so we are not blocked 
     // 
     ThreadPool.QueueUserWorkItem(new WaitCallback(ListenOnThread)); 
    } 

    /// <summary> 
    /// Stop Listening 
    /// </summary> 
    public void StopListening() 
    { 
     VerifyListening(); 
     // Set the stopEvent. This will cause the worker thread to close our and 
     // dispose of the HttpListener and exit the thread 
     // 
     this.stopEvent.Set(); 
    } 

    /// <summary> 
    /// Thread pool method to start listening on the background thread 
    /// </summary> 
    /// <param name="state">State - ignore</param> 
    /// 
    private void ListenOnThread(object state) 
    { 
     using (HttpListener listener = new HttpListener()) 
     { 
      listener.Prefixes.Add(
       String.Format(
       "http://+:{0}/PushNotificationsClient/", 
       this.portNumber.ToString())); 
      listener.Start(); 
      this.isListening = true; 
      while (!shouldStop) 
      { 
       IAsyncResult asyncResult = listener.BeginGetContext(
       AsyncCallbackMethod, listener); 
       // Wait on either the listener or the stop event 
       // 
       int index = WaitHandle.WaitAny(
       new WaitHandle[] { stopEvent, asyncResult.AsyncWaitHandle }); 
       switch (index) 
       { 
        case 0: 
         // Stop event was triggered. 
         // 
         shouldStop = true; 
         break; 
        case 1: 
         // Notification was received. Just loop around so we can call 
         // BeginGetContext again 
         // 
         break; 
       } 
      } 
      listener.Stop(); 
     } 
     this.isListening = false; 
    } 

    /// <summary> 
    /// Async method called once we receive a request 
    /// </summary> 
    /// <param name="result">Async result containing our HttpListener</param> 
    /// 
    private void AsyncCallbackMethod(IAsyncResult result) 
    { 
     HttpListener listener = result.AsyncState as HttpListener; 
     if (!this.isListening) 
     { 
      // Our callback gets fired when we stop the listener too. If it is not 
      // listening, just return. 
      // 
      return; 
     } 
     HttpListenerContext context = listener.EndGetContext(result); 
     SendNotificationResponseType request; 
     // Now use the XML serializer to turn the XML into a notification 
     // serialization type... 
     // 
     XmlDocument doc = new XmlDocument(); 
     try 
     { 
      doc.LoadXml(
      new StreamReader(
      context.Request.InputStream).ReadToEnd()); 
      // retrieve the first SendNotification element (there should be only one). 
      // 
      XmlNodeList nodes = doc.SelectNodes("//m:SendNotification[1]", this.mgr); 
      if (nodes.Count == 0) 
      { 
       // this wasn't a SendNotification request or it was malformed or 
       // something like that. 
       FailRequest(context); 
       return; 
      } 
      string sendNotification = nodes[0].OuterXml; 
      using (MemoryStream ms = new MemoryStream()) 
      { 
       byte[] bytes = Encoding.UTF8.GetBytes(sendNotification); 
       ms.Write(bytes, 0, bytes.Length); 
       ms.Flush(); 
       ms.Position = 0L; 
       request = (SendNotificationResponseType)this.ser.Deserialize(ms); 
      } 
     } 
     catch (XmlException) 
     { 
      // Failed to deserialize request. 
      // 
      FailRequest(context); 
      return; 
     } 
     // Fire the delegate 
     // 
     NotificationResponse response = eventHandler(
     this, /* sender */ 
     request.ResponseMessages.Items[0] 
     as SendNotificationResponseMessageType); 
     GenerateResponseXML(context, response); 
    } 

    /// <summary> 
    /// Fail the request. Right now we don't differentiate between reasons why it 
    /// failed. 
    /// </summary> 
    /// <param name="context">Request context</param> 
    /// 
    private void FailRequest(HttpListenerContext context) 
    { 
     context.Response.ContentEncoding = Encoding.UTF8; 
     context.Response.ContentType = "text/xml; charset=utf-8"; 
     context.Response.ProtocolVersion = new Version(1, 1, 0, 0); 
     context.Response.StatusCode = 400; 
     string response = "<?xml version=\"1.0\"?>" + 
     "<Error>Bad Request</Error>"; 
     byte[] responseBytes = Encoding.UTF8.GetBytes(response); 
     context.Response.ContentLength64 = responseBytes.Length; 
     context.Response.OutputStream.Write(
     responseBytes, 0, responseBytes.Length); 
     context.Response.OutputStream.Flush(); 
    } 

    /// <summary> 
    /// Generate the response xml 
    /// </summary> 
    /// <param name="context">call context</param> 
    /// <param name="response">The response enum value</param> 
    /// 
    private void GenerateResponseXML(
    HttpListenerContext context, 
    NotificationResponse response) 
    { 
     StringBuilder builder = new StringBuilder(); 
     builder.AppendLine("<?xml version=\"1.0\"?>"); 
     builder.AppendLine("<s:Envelope xmlns:s= " + 
     "\"http://schemas.xmlsoap.org/soap/envelope/\">"); 
     builder.AppendLine("<s:Body>"); 
     builder.AppendLine(" <SendNotificationResult " + 
     "xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\">"); 
     builder.AppendFormat(" <SubscriptionStatus>{0}</SubscriptionStatus>\r\n", 
     response.ToString()); 
     builder.AppendLine(" </SendNotificationResult>"); 
     builder.AppendLine("</s:Body>"); 
     builder.AppendLine("</s:Envelope>"); 
     context.Response.ContentEncoding = Encoding.UTF8; 
     context.Response.ContentType = "text/xml; charset=utf-8"; 
     context.Response.ProtocolVersion = new Version(1, 1, 0, 0); 
     context.Response.StatusCode = 200; 
     byte[] responseBytes = Encoding.UTF8.GetBytes(builder.ToString()); 
     context.Response.ContentLength64 = responseBytes.Length; 
     context.Response.OutputStream.Write(
     responseBytes, 0, responseBytes.Length); 
     context.Response.OutputStream.Flush(); 
    } 

    /// <summary> 
    /// Returns true if the listener is listening 
    /// </summary> 
    public bool IsListening 
    { 
     get 
     { 
      return isListening; 
     } 
    } 
    /// <summary> 
    /// Verifies that the listener isn't listening 
    /// </summary> 
    private void VerifyNotListening() 
    { 
     if (isListening) 
     { 
      throw new PushNotificationStateException("Cannot perform this operation " + 
      "when listening"); 
     } 
    } 

    /// <summary> 
    /// Verifies that the listener is listening 
    /// </summary> 
    private void VerifyListening() 
    { 
     if (!isListening) 
     { 
      throw new PushNotificationStateException("Cannot perform this operation " + 
      "when not listening"); 
     } 
    } 


} 
+0

你不是以聰明的方式提出這個問題。這個例外對你來說可能並不意味着什麼,但對我們來說確實如此發佈消息+堆棧跟蹤。 – 2010-11-19 17:03:50

回答

2

這將幫助,如果你告訴我們哪裏在初始化它失敗了,但我懷疑你要麼試圖註冊,你有沒有與netsh命令啓用了URI,或者你試圖註冊一些其他進程已經註冊的URI。

Documentation該異常說,部分:

HttpListenerException會拋出 如果HttpListener嘗試 註冊一個統一資源標識符 (URI)前綴已經 註冊。

是否有其他程序已經註冊http://+:{0}/PushNotificationsClient/?你是否記得運行netsh命令在正確的端口上註冊URI並允許監聽?

+0

嗨,不,我沒有運行netsh命令,我剛剛瞭解到這一點,因爲我們從來沒有在Windows 2008 Server R1機器上使用它。將在早上嘗試第一件事,謝謝! – hoetz 2010-11-20 19:53:30

相關問題