мы создали прослушиватель push-уведомлений веб-службы Exchange на основе HTTPListener в книге EWS (код ниже). Он отлично работал на Win 2008 Server с одноядерным Xeon. Теперь мы переместили его на Win 2008 R2 Server с четырехъядерным процессором Opteron, и он сразу же вышел из строя после инициализации прослушивателя с 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");
}
}
}