【问题标题】:HTTPListener crashes on quad core machine Win 2008 Server R2HTTPListener 在四核机器 Win 2008 Server R2 上崩溃
【发布时间】:2011-05-12 17:34:24
【问题描述】:

我们基于 EWS 书中的 HTTPListener 创建了一个 Exchange Web Service Push Notifications Listener(代码如下)。它在带有单核至强的 Win 2008 服务器上运行良好。现在我们将它移到一个带有四核 opteron CPU 的 Win 2008 R2 服务器上,它在使用 HTTPListenerExceptions 初始化监听器后立即崩溃。现在因为除了服务器没有任何改变,我认为它可能与多线程有关。或许有人能给点建议,谢谢。

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


}

【问题讨论】:

  • 你问这个问题的方式并不聪明。例外可能对您没有任何意义,但对我们却有。发布消息 + 堆栈跟踪。

标签: c# multithreading service windows-server-2008 httplistener


【解决方案1】:

如果您告诉我们初始化失败的位置会有所帮助,但我怀疑您正在尝试注册尚未使用 netsh 命令启用的 URI,或者您正在尝试注册一个其他进程已经注册的 URI。

Documentation 表示该异常的部分原因是:

HttpListenerException 将被抛出 如果HttpListener 试图 注册统一资源标识符 (URI) 前缀已经是 已注册。

其他进程是否已经注册http://+:{0}/PushNotificationsClient/?您还记得运行netsh 命令在正确的端口上注册 URI 并允许侦听吗?

【讨论】:

  • 嗨,不,我没有运行 netsh 命令,我刚刚了解到这一点,因为我们从未在 Windows 2008 Server R1 机器上使用它。早上第一件事就试试,谢谢!
猜你喜欢
  • 2012-09-18
  • 2018-03-17
  • 1970-01-01
  • 2011-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多