.NET Framework- und C#-Unterstützung für MSMQ

Veröffentlicht: 05. Sep 2002 | Aktualisiert: 10. Nov 2004

Von Jim Chundevalel

In diesem Artikel befasst sich Jim Chundevalel mit der nahtlosen Zusammenarbeit zwischen dem .NET Framework und MSMQ (Microsoft Message Queue), einem integralen Bestandteil des Windows DNA Frameworks. Es werden einige grundlegende Konzepte der Integration von .NET-Anwendungen in MSMQ erläutert. Sie lernen die Anatomie einer Chatanwendung kennen, die mit MSMQ Nachrichten übermittelt. Daneben erfahren Sie mehr über die Fähigkeiten von MSMQ und darüber, wie diese mithilfe von C# und .NET Framework möglichst weit reichend eingebunden werden können, so dass Sie Ihre Anwendungen auf einfache Weise mit Funktionen für die Nachrichtenübermittlung ausstatten können.

 

Zur Homepage von Wrox

Diesen Artikel können Sie dank freundlicher Unterstützung von Wrox auf MSDN Online lesen. Wrox ist ein Partner von MSDN Online.

 

Zur Partnerübersichtsseite von MSDN Online

Verwandte ASPToday-Artikel

Verwandte Quellen

Das .NET Framework arbeitet nahtlos mit den Komponenten des Windows DNA Frameworks (Distributed Internet Architecture) zusammen. Damit steht dem Entwickler eine umfangreiche Sammlung an Klassenbibliotheken zur Verfügung, die es ihm ermöglicht, Anwendungen mit den vertrauten Technologien zu verzahnen - und MSMQ ist eine davon.

Für diesen Artikel habe ich mich im Detail mit einer solchen Instanz befasst - mit der nahtlosen Zusammenarbeit zwischen dem .NET Framework und MSMQ, einem integralen Bestandteil des Windows DNA Frameworks.

Der Artikel erläutert einige grundlegende Konzepte der Einbindung von .NET-Anwendungen in MSMQ. Sie lernen die Anatomie einer Chatanwendung kennen, die mit MSMQ Nachrichten übermittelt. Daneben erfahren Sie mehr über die Fähigkeiten von MSMQ und darüber, wie diese mithilfe von C# und .NET Framework möglichst weit reichend eingebunden werden können, so dass Sie Ihre Anwendungen auf einfache Weise mit Funktionen für die Nachrichtenübermittlung ausstatten können.

Die Anwendung "MQChat"

MQChat ist eine Windows-Forms-Anwendung, die in C# mithilfe der neuesten Version des .NET Frameworks entwickelt wurde. Mit dieser Anwendung können registrierte Benutzer Nachrichten über die MSMQ-Nachrichtenübermittlungsfunktionen austauschen. Die Anwendung MQChat verfügt über vier Formulare (Fenster), von denen sich jedes weitest gehend auf den System.Windows.Forms-Namespace für die Benutzeroberfläche und den System.Messaging-Namespace für die Kernfunktionen stützt.

Ich habe mich für C# als Programmiersprache entschieden. Wie Sie jedoch wissen, erübrigen sich mit der Common Language Runtime (CLR) und dem zugehörigen Common Type System (CTS) des .NET Frameworks die viel diskutierten Probleme mit den Sprachbarrieren, so dass Sie als Programmierer beliebige Teile der Anwendung in einer Sprache Ihrer Wahl (VB.NET, Jscript.NET usw.) umschreiben können.

Systemanforderungen
Sie haben zwei Möglichkeiten, MQChat zu installieren:

Testinstallation
Wenn Sie MQChat zu Testzwecken installieren möchten, benötigen Sie einen einzelnen Computer mit einer unabhängigen Clientinstallation von MSMQ. Unabhängige Clients verfügen über lokale Warteschlangen, und die Anwendung kann getestet werden, indem mehrere Instanzen der Anwendung auf einem Computer ausgeführt werden. Die Systemanforderungen für eine unabhängige Clientinstallation auf einem Computer sind wie folgt:

  • Windows 2000 Professional/ Windows XP Professional

  • Unabhängige Clientinstallation von MSMQ

  • .NET Framework SDK

  • SQL Server 7.0/2000 oder MSDE

Reale Bereitstellung
Im Falle einer realen Bereitstellung sollten Sie über eine abhängige Clientinstallation von MSMQ verfügen. In einem Szenario für die reale Bereitstellung stellt jeder Clientcomputer die Verbindung zu einem Message-Queue-Server her und hat Zugriff auf die öffentlichen Warteschlangen, die vom Message-Queue-Server verwaltet werden.
Die Systemanforderungen für eine reale Bereitstellung sind wie folgt:

  • Server - Windows 2000 Server, MSMQ 2.0

  • Datenbankserver - SQL Server 7.0/2000

  • Client - Windows 2000 Professional/ Windows XP Professional, .NET Framework SDK, abhängige MSMQ 2.0-Clientinstallation

Bevor wir uns näher mit den Entwicklungsdetails von MQChat auseinander setzen, folgen zunächst Informationen zu den Hintergründen von MSMQ.

MSMQ - Der technische Hintergrund

Messaging ist nicht unbedingt eine Neuentwicklung, sondern war schon in der Zeit der einstufigen Anwendungen gängige Praxis. Allerdings ist die Ära der einstufigen Anwendungen längst Vergangenheit, und wir befinden uns heute im Zeitalter der Anwendungen, die zunächst unverbunden sind und über unzuverlässige oder unbekannte Verbindungen miteinander kommunizieren müssen.

Dies bezieht sich natürlich nicht auf Anwendungen, die intern über das Netzwerk ausgeführt werden - hier müssen Sie sich über die Anwendungskommunikation keine Sorgen machen, denn Sie können ziemlich sicher sein, dass das Netzwerk funktioniert und einen Fehler ggf. jederzeit beheben. Was ist jedoch mit den modernen, verteilten Anwendungen, die u. U. mit einem Server auf der anderen Seite des Globus kommunizieren müssen, um auf ein wichtiges Geschäftsobjekt zugreifen zu können? Wie können Sie dabei sicher sein, dass die Kommunikationsverbindung ordnungsgemäß funktioniert?

Nehmen wir zum Beispiel eines der typischen TV-Versandhäuser. Sie sehen ein Produkt im Fernsehen, rufen die angegebene Nummer an und bestellen das gewünschte Produkt. In der Regel müssen die Auftragsverarbeitungssysteme solcher Unternehmen also rund um die Uhr und an sieben Tagen die Woche verfügbar sein. Was passiert jedoch, wenn das Datenbanksystem ausfällt?

In diesem Fall muss das Vertriebsteam die Aufträge manuell aufnehmen, was nicht nur fehleranfällig ist, sondern auch außerordentlich ermüdend sein kann. Alternativ wäre es natürlich großartig, wenn ein System vorhanden wäre, das alle bis zur erneuten Inbetriebnahme des Datenbanksystems eingehenden Aufträge in einem zentralen Repository speichert, so dass diese Aufträge nicht verloren gehen, sondern anschließend dem zentralen Repository entnommen und regulär verarbeitet werden können.

MSMQ befasst sich mit solchen Überlegungen - garantierte einmalige Übergabe, verbindungsloses Messaging, priorisiertes Messaging usw. Messaging vereinfacht für Anwendungen die schnelle und zuverlässige Kommunikation mit anderen Anwendungen durch den Austausch von Nachrichten. Messaging ist zudem robust und ausfallsicher. Für das oben stehende Beispiel heißt das: Auch für diesen Fall würde ein System, das mit Messaging arbeitet, den Anforderungen in jeder Hinsicht gerecht. Wenn das Datenbanksystem ausfällt, speichert das Messagingsystem alle eingehenden Aufträge in einer Warteschlange und verhindert damit deren Verlust, und sobald der Datenbankserver wieder läuft, werden die Aufträge verarbeitet und aus der Warteschlange entfernt.

Message Queuing oder MSMQ ist die Messagingtechnologie von Microsoft, die die Nachrichtenübermittlung und das Speichern von Nachrichten in Warteschlangen für Anwendungen und Computer ermöglicht, die auf der Windows-Plattform ausgeführt werden - unabhängig davon, ob es sich um ein oder mehrere Netzwerke handelt. Die MSMQ-Funktionen ermöglichen das Senden von Nachrichten zwischen zwei Computern (wobei eine Nachricht als eine Dateneinheit betrachtet wird).

Eine Nachricht kann alles sein - eine einfache Textnachricht ebenso wie eine komplexe Nachricht, die aus eingebetteten Objekten besteht. Nachrichten werden an Warteschlangen gesendet, also an Container, die die Nachrichten speichern, während sie sich auf dem Übertragungsweg befinden. Die Hauptaufgabe einer Nachrichtenwarteschlange besteht darin, die Übermittlung zu garantieren - wenn die Empfängeranwendung nicht erreichbar ist und die Nachricht daher nicht empfangen kann, wird diese in der Warteschlange zwischengespeichert, bis eine erfolgreiche Übermittlung möglich ist. Bei Warteschlangen wird zwischen zwei Hauptkategorien unterschieden: zwischen benutzerseitig und systemseitig generierten Warteschlangen.

Die benutzerseitig generierten Warteschlangen werden wiederum in folgende Typen unterteilt:

  • Öffentliche Warteschlangen - Diese Warteschlangen werden über das Message Queuing-Netzwerk repliziert und stehen allen qualifizierten Mitgliedern des Netzwerks zur Verfügung.

  • Private Warteschlangen - Diese Warteschlangen stehen nur auf dem lokalen Computer zur Verfügung, auf dem MSMQ installiert ist. Über das Message Queuing-Netzwerk kann nicht darauf zugegriffen werden.

  • Administrationswarteschlangen - Diese Warteschlangen werden zum Speichern von Verwaltungsmeldungen verwendet wie Bestätigungsmeldungen, mit denen der Empfang von Nachrichten quittiert wird.

  • Antwortwarteschlangen - Diese Warteschlangen enthalten Antwortmeldungen, die von der Empfängeranwendung an die Senderanwendung übermittelt werden, um den Erhalt von Nachrichten zu quittieren.

Die systemseitig generierten Warteschlangen werden wie folgt klassifiziert:

  • Journalwarteschlangen - Diese Warteschlangen können verwendet werden, um Kopien von Nachrichten zu speichern, die an eine Warteschlange gesendet oder aus einer Warteschlange entfernt wurden.

  • Warteschlangen für unzustellbare Nachrichten - Diese Warteschlangen enthalten Kopien von unzustellbaren Nachrichten. Für das Speichern von unzustellbaren Transaktionsmeldungen wird eine spezielle Warteschlange verwendet, die als Warteschlange für unzustellbare Transaktionsmeldungen bezeichnet wird.

  • Berichtwarteschlangen - Diese Warteschlangen enthalten Meldungen, die die Route beschreiben, die eine Nachricht bis zum Erreichen des Ziels von der Nachrichtenquelle aus genommen hat.

  • Private Systemwarteschlangen - Diese Warteschlangen enthalten administrative und Benachrichtigungsmeldungen, die das System zum Verarbeiten von Messagingaktionen benötigt.

Das interessantere Thema ist aber die Warteschlangenkommunikation. Die Kommunikation zwischen Nachrichtenwarteschlangen verläuft im Grunde genommen asynchron - eine Anwendung sendet als ein Prozess Nachrichten an eine Warteschlange, und die Nachrichten in der Warteschlange werden von einer anderen Anwendung wiederum als separater Prozess empfangen.

Allerdings kann der Empfang der Nachrichten synchron oder asynchron verlaufen. Im asynchronen Modus kann die Anwendung, die die Nachrichten empfängt, eine asynchrone Empfangsoperation aufrufen und dann mit anderen Verarbeitungsaufgaben fortfahren - sie muss nicht darauf warten, dass die Nachricht empfangen wird. Im Falle eines synchronen Nachrichtenempfangs sollte der Empfänger nach der Erzeugung der Anforderung zum Erhalt von Nachrichten warten, bis die Nachricht empfangen wurde.

Messaging bietet, kurz gesagt, einen leistungsfähigen und flexiblen Mechanismus für die Interprozesskommunikation zwischen den unterschiedlichen Komponenten einer Anwendung. Gegenüber dem direkten Anruf von Komponenten bietet Messaging eine Reihe von Vorteilen wie Stabilität, Priorisierung, Offlineoperationen, Transaktionen, Nachrichtensicherheit, Journalerzeugung, Protokollierung usw. Per Messaging übermittelte Nachrichten sind von Komponentenausfällen in wesentlich geringerem Maße betroffen als per Direktverbindung versendete Nachrichten, da erstere in Warteschlangen gespeichert werden und dort bis zu ihrer Verarbeitung verbleiben - auf diese Weise kann die Übermittlung immer garantiert werden.

Die Offlineverarbeitung kann auf einfache Weise in Anwendungen integriert werden, indem Nachrichten an temporäre Warteschlangen gesendet werden, in denen sie bis zur erfolgreichen Übermittlung gespeichert werden. Messaging unterstützt zudem Transaktionen, bei denen mehrere zusammenhängende Nachrichten in einer Transaktion zusammengefasst werden, wodurch sichergestellt wird, dass die Nachrichten, wenn sie schließlich übermittelt werden, jeweils nur einmal und in der gesendeten Reihenfolge übermittelt werden. Findet keine Übermittlung statt, wird die gesamte Transaktion abgebrochen. Und nicht zuletzt ist Messaging eng in die Windows-Systemsicherheit integriert, wodurch Zugriffssteuerung, Benutzerauthentifizierung und Verschlüsselung von Nachrichten ermöglicht werden.

Definition des Problems

Heute gibt es eine Vielzahl von Möglichkeiten, um miteinander zu kommunizieren. Hierzu gehören auch die internetbasierten Chatanwendungen, aber wenn Kommunikation in einem Intranetszenario stattfinden soll, wird eigentlich keine internetbasierte Chatsoftware benötigt. Stattdessen stellt ein Lösung, die auf lokale Ressourcen wie lokal vorhandene Server und die Ressourcen an den Knoten zurückgreift, eine wesentlich bessere Alternative dar, denn so können Ausfälle der Internetverbindungen die Benutzerkommunikation im jeweiligen Netzwerk nicht unterbrechen.

MQChat ist ein Beispiel für eine solche Anwendung, die lokale Ressourcen nutzt, um die Echtzeitkommunikation zwischen Benutzern in einem Netzwerk oder, genauer gesagt, in einem Windows-Netzwerk, zu ermöglichen. MQChat ist eine Chatanwendung, die sich auf MSMQ 2.0 als Backbone stützt. Alle Nachrichten, die zwischen den einzelnen Benutzern ausgetauscht werden, werden in entsprechenden Warteschlangen gespeichert und von den jeweiligen Clientanwendungen verarbeitet. Mit der Verarbeitungsweise der Anwendung werden wir uns in Kürze noch genauer befassen.

Definition der Lösung

MQChat ist eine mithilfe von C# und dem .NET Framework entwickelte Windows Forms-Anwendung, die auf die Fähigkeiten von MSMQ zurückgreift, um die Echtzeitkommunikation zwischen registrierten Benutzern eines Windows-Netzwerks zu vereinfachen. Die Benutzeroberfläche der Anwendung setzt sich aus vier Formularen (Fenstern) zusammen. Diese Formulare stellen die Komponenten der Benutzeroberfläche bereit, die die Benutzer für die Registrierung, Anmeldung und die Arbeit mit MQChat benötigen. Bevor wir uns mit dem Layout der Benutzeroberfläche befassen, sollten wir uns zunächst mit dem Datenbankentwurf auseinander setzen, den MQChat voraussetzt.

Datenbankentwurf

Die von MQChat benötigte Datenbank arbeitet mit nur einer Tabelle - User_Info. In dieser Tabelle werden die Informationen der registrierten Benutzer von MQChat gespeichert. Die Datenbank weist die folgende Struktur auf:

Bild01

Die Datenbank kann sich auf dem gleichen Computer wie der Message-Queue-Server oder auf einem separaten Datenbankserver befinden. Im Folgenden sollten wir uns mit dem Layout der Benutzeroberfläche von MQChat beschäftigen.

Layout der Benutzeroberfläche

MQChat greift auf die Windows-Forms-Bibliothek zur Implementierung der Benutzeroberfläche und auf die Messaging-Bibliothek zum Implementieren der Funktionalität zu. Die Anwendung setzt sich aus den vier folgenden Formularen zusammen:

  • Splash.cs - Dieses Formular dient als Startfenster der Anwendung und bietet dem Benutzer die Möglichkeit, sich bei MQChat anzumelden.

  • Login.cs - Dieses Formular dient als Anmeldebildschirm für registrierte Benutzer von MQChat.

  • NewUser.cs - Mit diesem Formular können sich neue Benutzer registrieren.

  • Chat.cs - Dies ist das eigentliche Chatfenster der Anwendung, in dem registrierte Benutzer Nachrichten austauschen können. Darüber hinaus wird eine Transkription des Chats zwischen dem angemeldeten Benutzer und anderen Benutzern des Systems angezeigt.

Im Folgenden wollen wir uns im Detail mit dem Layout und dem Aufbau der Anwendung befassen.

Splash.cs

Dieses Formular dient als Start- bzw. Begrüßungsbildschirm von MQChat. Im Formular werden zwei LinkLabel-Objekte verwendet, die auf Basis der System.Windows.Forms.LinkLabel-Klasse generiert wurden. Diese Linkbezeichnungen sind mit den entsprechenden Ereignishandlern verknüpft, so dass dem Benutzer im Begrüßungsbildschirm die Optionen Login (Anmelden) und Exit (Beenden) zur Verfügung stehen. Nachstehend eine Abbildung des Begrüßungsbildschirms:

Bild02

Login.cs

Mit diesem Formular hat der Benutzer die Möglichkeit, sich bei MQChat anzumelden. Auch hier wird ein LinkLabel-Objekt verwendet, das für den Fall, dass sich die zugreifende Person als Benutzer von MQChat registrieren möchte, als Verknüpfung zum Benutzerregistrierungsformular fungiert. Hier die Bildschirmabbildung dieses Formulars:

Bild03

Wenn der Anmeldedialog angezeigt wird, kann ein registrierter Benutzer seinen Anmeldenamen und das Kennwort eingeben, um sich bei MQChat anzumelden. Wenn der Benutzer auf die Schaltfläche Login (Anmelden) klickt, werden die Anmeldeinformationen anhand der MQChat-Datenbank geprüft. Das nun folgende Benutzerauthentifizierungsverfahren ist relativ einfach - wenn der Benutzer auf die Schaltfläche Login geklickt hat, wird eine SQL-Anweisung mit dem angegebenen Benutzernamen und Kennwort erzeugt und an die Back-End-Datenbank übergeben. Wenn die Abfrage einen Datensatz zurückgibt, wird die Authentifizierung als erfolgreich betrachtet, andernfalls wird die entsprechende Fehlermeldung angezeigt:

Using System.Data.SqlClient
.
String strSQL = "SELECT * FROM USER_INFO WHERE LOGIN_NAME = '" + txtLogin.Text.Trim
().ToUpper() + "' AND PASSWORD = '" + txtPassword.Text + "'";
bool _isAuthenticated = false;
SqlConnection myConnection = new SqlConnection(strConnString);
SqlCommand myCommand = new SqlCommand(strSQL , myConnection);
try
{
      myConnection.Open();
      SqlDataReader myReader = myCommand.ExecuteReader();
      //If a record exists, then the login is correct
      while(myReader.Read())
{
            if(myReader.GetString(0) == txtPassword.Text)
{
                  _isAuthenticated = true;
            }
      }
      myReader.Close();
      myCommand.Dispose();
}
catch(SqlException myExcept)
{
MessageBox.Show(myExcept.Message , "MQ Chat 1.0" , MessageBoxButtons.OK , MessageBoxIcon.Error);
}
finally
{
      myConnection.Close();
//Since the login was correct, take the user to his/her chat window;
      if(_isAuthenticated == true)
      {
            //Code to display the chat window
      }
      //If no record exist, then the login was incorrect
      else
      {
//Code to display an error message
      }
}

Als nächstes wollen wir einen Blick auf das Benutzerregistrierungsformular werfen.

NewUser.cs

Hier die Bildschirmabbildung des Formulars NewUser.cs:

Bild04

Die diesem Formular zugrunde liegende Verarbeitung ist ebenfalls recht einfach, einmal abgesehen von einem neuen Teilbereich, mit dem wir uns in Kürze befassen werden. Dieses Formular akzeptiert die verschiedenen Datenelemente, die die neuen Benutzer eingeben. Zugleich wird die Back-End-Datenbank aktualisiert, indem jeder neue Benutzer eingetragen wird, so dass diese mit der Nutzung von MQChat beginnen können. Beim Generieren des Benutzerkontos erfolgen zudem die entsprechenden Duplikatprüfungen, um zu verhindern, dass ein Benutzername im System doppelt auftritt.

String strSQL = "SELECT USER_ID FROM USER_INFO WHERE 
LOGIN_NAME = '" + txtLogin.Text.Trim().ToUpper() + "'";
bool _isDuplicate = false;
bool _regComplete = false;
SqlConnection myConnection = new SqlConnection(strConnString);
SqlCommand myCommand = new SqlCommand(strSQL , myConnection);
try
{
       myConnection.Open();
       SqlDataReader myReader = myCommand.ExecuteReader();
       //Checking for duplicate user name
       while(myReader.Read())
       {
              _isDuplicate = true;
       }
       myReader.Close();
       myCommand.Dispose();
//If a duplicate user name is found, then don't create a record for the user.
       if(_isDuplicate == true)
       {
              //Display an error message indicating the duplication
       }
       else
       {
//Add a new record to the database
strSQL = "INSERT INTO USER_INFO (LOGIN_NAME, PASSWORD, FULL_NAME) VALUES ('" + 
txtLogin.Text.Trim().ToUpper() + "','" + txtPassword.Text + "','" + 
txtFullName.Text.Trim() + "')";
              myCommand = new SqlCommand(strSQL , myConnection);
              myCommand.ExecuteNonQuery();
              myCommand.Dispose();
}

Nun wenden wir uns dem interessanteren Teil zu, dem Speichern in der Warteschlange. Bevor wir uns jedoch mit den Einzelheiten befassen, möchte ich kurz umreißen, wie Messaging in MQChat funktioniert. Sobald sich ein neuer Benutzer bei MQChat registriert, wird auf dem entsprechenden Message-Queue-Server eine Nachrichtenwarteschlange für diesen Benutzer angelegt (im Falle eines einzelnen Computers wird diese als private Warteschlange, bei einer Serverinstallation als öffentliche Warteschlange auf dem Message-Queue-Server angelegt).

Wenn sich der Benutzer beim System anmeldet, wird er immer mit seiner Nachrichtenwarteschlange verbunden. Andere Benutzer, die eine Nachricht an diesen Benutzer senden möchten, senden die Nachricht nun an dessen Warteschlange, und die Nachricht wird sofort an den gewünschten Empfänger übermittelt. Ein Benutzer kann Nachrichten an eine beliebige Anzahl Benutzer senden und ebenso Nachrichten von einer beliebigen Anzahl Benutzer erhalten.

Für den Fall, dass die Zahl der Benutzer sehr groß wird, kann diese Lösung jedoch möglicherweise nicht entsprechend skaliert werden. In diesem Fall ist es sicher günstiger, eine einzige Warteschlange zu verwenden und das System so einzurichten, dass bei jeder Nachricht, die in der Warteschlange eingeht, die Empfängerinformationen in die Nachricht eingebettet sind. Jeder Client überwacht dann die Warteschlange und prüft jede eingehende Nachricht, während die eigentliche Nachrichtenübermittlung nur an den gewünschten Empfänger erfolgt.

Dieser Entwurf sorgt dafür, dass der Benutzer Nachrichten an eine beliebige Anzahl anderer Benutzer senden kann - er muss lediglich den Namen des gewünschten Benutzers aus der Liste wählen und die Nachricht senden. Ebenso kann jeder Benutzer Nachrichten von einer beliebigen Anzahl Benutzer empfangen.
Der System.Messaging-Namespace stellt eine reiche Auswahl an Klassen zur Verfügung, die es Ihnen ermöglichen, Nachrichtenwarteschlangen und Nachrichten programmgesteuert zu verwalten und zu bearbeiten.

Im Messaging-Namespace sind verschiedene Klassen wie Message, MessageQueue, MessageEnumerator, MessageQueueEnumerator, MessageQueueException usw. definiert, mit denen Programmschnittstellen für das Verwalten von Nachrichten und Nachrichtenwarteschlangen bereitstehen. Die im System.Messaging-Namespace enthaltene Komponente MessageQueue kann zum Initiieren neuer Warteschlangen im Message Queuing-Netzwerk verwendet werden.

Mithilfe der Create-Methode der Komponente MessageQueue können Sie eine Warteschlange anlegen, d. h. Sie können eine private Warteschlange auf dem lokalen Computer oder eine öffentliche Warteschlange auf einem beliebigen Message Queuing-Computer anlegen, für dessen Domäne Sie über Administratorrechte verfügen. Darüber hinaus können Sie auch öffentliche Warteschlangen auf dem lokalen Computer anlegen.

Der nächste Schritt im Formular NewUser.cs besteht also darin, eine Nachrichtenwarteschlange für neu registrierte Benutzer anzulegen, damit diese über eine Speicherposition verfügen, an der alle an sie gerichteten Nachrichten aufbewahrt werden. Nachfolgend der Code, mit dem eine Warteschlange für neu registrierte Benutzer initiiert wird:

using System.Messaging;
MessageQueue myQueue
//For a private queue in your local computer, use this line;
String strQueueServer = ".\\PRIVATE$\\"
//For a public queue in a message queuing computer for which you have domain rights, use this line
String strQueueServer = "SRVR\\"   \\ where SRVR is the name of the machine
try
{
myQueue = MessageQueue.Create(strQueueServer + txtLogin.Text.Trim().ToUpper());
       myQueue.Label = txtLogin.Text.Trim().ToUpper();
       myQueue.Close();
}
catch(Exception myExp)
{
}

Dieser Code verwendet eine Variable vom Typ System.Messaging.MessageQueue und führt die Create-Methode der MessageQueue-Klasse zum Erzeugen der Warteschlange aus. Damit enthält die myQueue-Objektvariable einen Verweis auf die neue Warteschlange. Der Name der Warteschlange entspricht dem Anmeldenamen des neuen Benutzers, damit erkennbar ist, welchem Benutzer die Warteschlange zuzuordnen ist.

Anschließend wird die Close-Methode des myQueue-Objekts aufgerufen, um die Warteschlange zu schließen und die vom Objekt belegten Ressourcen freizugeben. Je nach Installation der Nachrichtenwarteschlange können Sie eine private Warteschlange auf dem lokalen System oder eine öffentliche Warteschlange auf einem Message Queue-Server generieren. Die Art und Weise, wie die Create-Methode aufgerufen wird, ist grundsätzlich die gleiche - wird eine öffentliche Warteschlange generiert, wird zusätzlich lediglich der vollständige Pfad der Warteschlange übergeben.

MessageQueue.Create("MYSERVER\\MYQUEUE");

Anstelle des Computernamens können Sie auch einen Punkt (".") verwenden, wenn eine Warteschlange auf dem lokalen Computer erzeugt werden soll. Stellen Sie für eine öffentliche Warteschlange dem Warteschlangenpfad den Bezeichner PRIVATE$ voran.

messageQueue.Create(".\PRIVATE$\MYQUEUE");

Beachten Sie, dass das Windows-Konto für die Registrierung des Benutzers auf dem Message-Queue-Server über die entsprechenden Zugriffsrechte verfügen muss.

Chat.cs

Dieses Formular stellt den wichtigsten Abschnitt der Anwendung dar, denn dies ist der Bildschirm, von dem aus die Benutzer Nachrichten an andere Benutzer von MQChat senden und von diesen empfangen. Wenn Sie die in diesem Formular eingebettete Verarbeitungslogik näher betrachten, erfahren Sie auch einiges über die Nachrichtenverarbeitung und die unterschiedlichen Methoden, wie diese erfolgen kann.

Wir werden uns nun im Einzelnen mit Konzepten wie Verweisen auf Warteschlangen, Zusammenstellen von Nachrichten, Senden von Nachrichten an Warteschlangen usw. befassen. In diesem Zusammenhang werden Sie auch ein äußerst wichtiges Konzept von Messaging kennen lernen, das Serialisieren von Nachrichten. Darüber hinaus werden Sie Einzelheiten zu den unterschiedlichen verfügbaren Formatierungsobjekten erfahren, die für die Nachrichtenserialisierung zuständig sind.

Wie Sie bereits wissen, verfügt jeder registrierte Benutzer von MQChat über eine Warteschlange, die zum Zeitpunkt der Registrierung unter seinem Anmeldenamen angelegt wird. Wenn sich der Benutzer bei der Anwendung anmeldet, stellt die Anwendung die Bindung zu dieser Warteschlange her. Dies erfolgt, indem ein Verweis auf die entsprechende Warteschlange festgelegt wird oder, anders ausgedrückt, indem eine Instanz der MessageQueue-Komponente hergestellt und mithilfe des Pfadnamens dafür gesorgt wird, dass diese Instanz auf die gewünschte Warteschlange verweist. Der Code hierfür sieht folgendermaßen aus:

//In case of a private queue
MessageQueue myQueue = new MessageQueue(".\\PRIVATE$\\" + strLoginName);
//in case of a public queue
MessageQueue myQueue = new MessageQueue("SRVR\\"+strLoginName);

Dabei steht strLoginName für eine Variable vom Typ Zeichenfolge, die den Anmeldenamen des angemeldeten Benutzers und damit auch den Namen der Warteschlange enthält, die diesem Benutzer zugeordnet wurde (die Bezeichnung der Warteschlange entspricht dem Anmeldenamen des Benutzers). Betrachten Sie die folgende Bildschirmabbildung:

Bild05

Wie Sie sehen, enthält das Formular eine Liste aller registrierten Benutzer des Systems (im Dropdown-Listenfeld), so dass Sie einen Benutzer auswählen und dann die Nachricht eingeben können. Anschließend klicken Sie auf die Schaltfläche Send (Senden), um die Nachricht zu versenden. So wird die Nachricht an die Warteschlange des von Ihnen ausgewählten Benutzers gesendet.

Eine Nachricht besteht im Grunde genommen aus einem Nachrichtenkörper (der die eigentliche Nachricht enthält) sowie einer Reihe zugehöriger Eigenschaften. Der Nachrichtenkörper kann beliebige Informationen enthalten, wobei die Größe jedoch 4 MB nicht überschreiten darf. Einfache Nachrichten (wie normaler Text oder ein ganzzahliger Wert) können an eine Warteschlange gesendet werden, indem die Send-Methode der MessageQueue-Komponente direkt mit dem gewünschten Wert aufgerufen wird:

MessageQueue.Send("Test Message");

Wenn Sie jedoch über weitere Steuerungsmöglichkeiten für die zu sendenden Nachrichten verfügen möchten, empfiehlt es sich, ein Message-Objekt zu generieren und die Nachricht durch Bearbeiten der unterschiedlichen Eigenschaften des Objekts anzufertigen. Bevor Sie mehr über das Senden von Nachrichten erfahren, wollen wir uns jedoch noch kurz mit der Nachrichtenserialisierung befassen.

Im Allgemeinen wird mit Serialisierung das Verfahren bezeichnet, bei dem in einem Objekt befindliche Statusinformationen in eine Form umgewandelt werden, die gespeichert oder übertragen werden kann. Die Serialisierung ist nützlich, da sie zum erneuten Erzeugen von Objekten herangezogen werden kann, indem der Dauerstatus des Objekts gelesen (oder besser gesagt deserialisiert) wird.

Beim Speichern von Nachrichten in Warteschlangen bezieht sich der Begriff Serialisierung auf das Konvertieren eines Objekts oder einer Sammlung von Daten in eine Nachricht, die an eine Nachrichtenwarteschlange übertragen wird; bzw. auf das Zurückkonvertieren des Objekts in eine Sammlung von Daten, die die Anwendung dann verarbeiten kann. Für die Nachrichtenserialisierung in .NET benötigen Sie ein Formatierungsobjekt. Das Formatierungsobjekt ist für die Serialisierung eines Objekts oder von Daten in einen Strom zuständig, der an eine Nachrichtenwarteschlange gesendet werden kann. Ebenso ist das Formatierungsobjekt für das anschließende Deserialisieren des Stromes zurück in ein Objekt oder in Daten zuständig, die die Anwendung verarbeiten kann. Je nach Art der Daten, die Sie in Ihren Anwendungen senden und empfangen möchten, können Sie zwischen drei Arten von Nachrichtenformatierungen auswählen:

  • XMLMessageFormatter - Dies ist die standardmäßige Formatierungseinstellung für eine MessageQueue-Komponente. Hiermit werden Objekte und primitive Datentypen in XML-Zeichenfolgen konvertiert.

  • BinaryMessageFormatter - Dieses Formatierungsobjekt serialisiert Objekte in Binärdatenströme, die extrem schnell verarbeitet werden können.

  • ActiveXMessageFormatter - Dieses Formatierungsobjekt bietet Abwärtskompatibilität für Komponenten, die mit vorherigen Versionen von Message Queuing arbeiten. Dies wird als eine extrem schnelle Methode der Serialisierung betrachtet.

Für MQChat habe ich das XMLMessageFormatter-Objekt zum Serialisieren von Warteschlangennachrichten ausgewählt. Nun kommen wir zu dem Code, der beim Senden der Nachricht die eigentliche Arbeit erledigt:

System.Messaging.Message myMessage = new System.Messaging.Message();
MessageQueue myQueue = new MessageQueue();
try
{
       myMessage.Formatter = new XmlMessageFormatter();
       myMessage.Label = strLoginName;
       myMessage.Body = txtMsg.Text.Trim();
       myQueue.Path = strQueueServer+ cboUsers.SelectedItem.ToString().
ToUpper();myQueue.Send(myMessage);
       }

Wie Sie diesem Codeausschnitt entnehmen können, ist der eigentliche Vorgang ziemlich einfach. Die Formatter-Eigenschaft des Message-Objekts wird auf ein XMLMessageFormatter-Objekt festgelegt. Anschließend werden die anderen Eigenschaften der Nachricht wie Label (dadurch erfährt der Empfänger, wer die Nachricht gesendet hat) und der Nachrichtenkörper (Body) festgelegt. Nach dem Fertigstellen der Nachricht wird ein Verweis auf die Warteschlange des Empfängers generiert, und anschließend wird die Send-Methode der MessageQueue-Komponente aufgerufen.

Nun zum Empfangen von Nachrichten. Wie Sie bereits wissen, initiiert die Anwendung bei der Anmeldung des Benutzers einen Verweis auf die zugehörige Nachrichtenwarteschlange und hält diesen Verweis aktiv, bis die Anwendung beendet wird. Nun sollten wir die Anwendung für den Empfang von Nachrichten aus der Warteschlange vorbereiten. Dabei muss sicherstellt werden, dass die Anwendung über die gesamte aktuelle Laufzeit in der Lage ist, Nachrichten zu empfangen, sobald diese in der Warteschlange eingehen.

Darüber hinaus sollte der Nachrichtenempfang den Benutzer nicht daran hindern, Nachrichten an andere Benutzer zu senden, so dass wir mit einer asynchronen Empfangsmethode arbeiten müssen. Die Anwendung sollte also asynchron mit der Nachrichtenwarteschlange kommunizieren, um andere Prozesse nicht zu beeinträchtigen. Es ist zudem ratsam, sicherzustellen, dass nur jeweils eine Anwendung auf die Nachrichten in der entsprechenden Warteschlange zugreifen kann.

Legen Sie dazu für die DenySharedReceive-Eigenschaft der MessageQueue-Komponente true fest. So wird sichergestellt, dass jeweils nur eine Anwendung Nachrichten aus einer Warteschlange empfangen kann. Andere Anwendungen können erst dann auf die Warteschlange zugreifen, wenn die erste Anwendung die Warteschlange freigegeben hat (durch Aufruf der Close-Methode oder durch Garbage Collection):

myQueue.DenySharedReceive = true;

Im nächsten Schritt wird die Nachrichtenformatierung so eingestellt, dass Nachrichten, die von der Anwendung empfangen werden, in ein Format deserialisiert werden, das von der Anwendung verarbeitet werden kann. Dies ist die Aufgabe des nachstehenden Codeausschnitts:

myQueue.Formatter = new XmlMessageFormatter(new Type[]{typeof(String)});

Dieser Code legt für das Nachrichtenformatierungsobjekt der Warteschlange XMLMessageFormatter fest und gibt an, dass die XML-Nachrichten in den Datentyp String (Zeichenfolge) deserialisiert werden sollen.
Im nächsten Schritt wird der asynchrone Nachrichtenempfang gestartet, womit die Anwendung bereit zum Empfangen von Nachrichten ist, sobald diese eintreffen. Im Falle einer asynchronen Nachrichtenverarbeitung wird die Methode, mit der der Empfang gestartet wird, sofort zurückgegeben, ohne auf ein Ergebnis zu warten.

Wenn Sie Nachrichten asynchron empfangen möchten, verwenden Sie die Methoden BeginReceive und EndReceive der MessageQueue-Komponente. BeginReceive kennzeichnet den Beginn und EndReceive das Ende der Operation. Wenn Sie die BeginReceive-Methode aufrufen, wird diese unverzüglich zurückgegeben und löst ein Ereignis mit Namen ReceiveCompleted auf, wenn in der Warteschlange eine Nachricht verfügbar wird oder wenn die zu empfangende Nachricht in der Warteschlange bereits vorhanden ist. Zu diesem Zweck programmieren Sie einen Ereignishandler, der das ReceiveCompleted-Ereignis bei Auftreten verarbeitet - Sie entwickeln also eine Methode, die die Nachrichtenverarbeitung steuert und eine Meldung zurückgibt, wenn die Verarbeitung abgeschlossen ist.

Diese Methode weisen Sie als Ereignishandler dem ReceiveCompleted-Ereignis zu. Das ReceiveCompleted-Ereignis gibt ein Objekt vom Typ IAsyncResult zurück, das alle erforderlichen Informationen über den Nachrichtenempfangsvorgang enthält. Nachdem das Ereignis abgeschlossen ist, wird die EndReceive-Methode aufgerufen, um das Ende des asynchronen Nachrichtenempfangs zu kennzeichnen. Ist doch ganz einfach, oder? Betrachten Sie den folgenden Code, der diese Aufgabe der asynchronen Nachrichtenverarbeitung übernimmt:

MessageQueue myQueue = new MessageQueue(".\\PRIVATE$\\" + strLoginName);
myQueue.DenySharedReceive = true;
myQueue.Formatter = new XmlMessageFormatter(new Type[]{typeof(String)});
myQueue.ReceiveCompleted += new ReceiveCompletedEventHandler(onReceiveMessage);
myQueue.BeginReceive();

Beachten Sie vor allem die Vorgehensweise bei der Programmierung: Dem ReceiveCompleted-Ereignis wird ein Ereignishandler in Form eines Delegaten zugewiesen, und anschließend wird die BeginReceive-Methode aufgerufen, womit der asynchrone Nachrichtenempfang ausgelöst wird. Nun ein Blick auf den Ereignishandler:

protected void onReceiveMessage(Object mySender , ReceiveCompletedEventArgs myArgs)
{
MessageQueue myMsgQueue = (MessageQueue)mySender;
       System.Messaging.Message myMsg = myMsgQueue.EndReceive(myArgs.AsyncResult);
       myMsg.Formatter = new XmlMessageFormatter(new Type[]{typeof(String)});
       //Writing the messaging to the user's screen
       if(txtTranscript.Text.Trim() == "")
       {
              txtTranscript.Text = myMsg.Label + " : " + (String)myMsg.Body;
       }
       else
       {
txtTranscript.Text = txtTranscript.Text + "\r\n" + myMsg.Label + " : " + (String)myMsg.Body;
       }
       myMsgQueue.BeginReceive();
}

Beachten Sie besonders die Parameter, die an diesen Ereignishandler übergeben werden: das mySender-Objekt, das für das Objekt steht, das dieses Ereignis ausgelöst hat, und die ReceiveCompletedEventArgs-Klasse. Die ReceiveCompletedEventArgs-Klasse liefert die erforderlichen Daten für das ReceiveCompleted-Ereignis. Wenn der asynchrone Empfangsvorgang den Ereignishandler aufruft, wird eine Instanz dieser Klasse an den Ereignishandler übergeben. Die zum ReceiveCompleted-Ereignis zugehörigen Daten befinden sich im AsyncResult-Parameter des Delegaten.

Wie Sie im Code weiter oben sehen, enthält die AsyncResult-Eigenschaft der myArgs-Klasse die serialisierte Nachricht. Daher weisen wir diese einem Messaging.Message-Objekt zu und legen dann die Formatter-Eigenschaft des Message-Objekts auf XmlMessageFormatter fest. Dadurch wird die Nachricht deserialisiert und in das Chattranskriptionstextfeld geschrieben.

Beachten Sie auch die letzte Zeile des Ereignishandlers - hier wird die BeginReceive-Methode des MessageQueue-Objekts erneut aufgerufen. Warum müssen wir so vorgehen? Die Antwort ist recht einfach. Die BeginReceive-Methode gibt nur eine einzige Nachricht zurück und beendet die Verarbeitung, wenn die Nachricht vollständig empfangen wurde. Unsere Anwendung soll jedoch kontinuierlich alle Nachrichten empfangen, sobald diese verfügbar sind. Daher wird die BeginReceive-Methode am Ende des Ereignishandlers erneut aufgerufen, so dass die Anwendung nach jeder abgeschlossenen Nachrichtenverarbeitung wieder in die Lage versetzt wird, die nächste verfügbare Nachricht zu verarbeiten.

Installieren von MQChat

Nach dem Download der ZIP-Datei und dem Extrahieren der vier Quellcodedateien (Splash.cs, Login.cs, NewUser.cs und Chat.cs) besteht der nächste Schritt im Aufbau der Datenbank.

Aufbau der Datenbank

  1. Öffnen Sie in SQL Server 7.0/SQL Server 2000 oder MSDE eine leere Datenbank. Nennen Sie diese Datenbank "ChatServer". (Sie können auch einen beliebigen anderen Namen Ihrer Wahl verwenden. In diesem Fall müssen Sie jedoch unbedingt diesen Teil der Verbindungszeichenfolge im Code ändern.)

  2. Öffnen Sie SQL Enterprise Manager, und führen Sie die Abfrage aus, die in der Datei MQChat.sql enthalten ist.

  3. Hiermit wird die erforderliche Tabelle in der Datenbank bereitgestellt.

Konfigurieren und Kompilieren der Quellcodedateien

  1. Der erste Schritt besteht darin, die erforderlichen Änderungen an der Datenbankverbindungszeichenfolge und am Namen des Message Queue-Servers vorzunehmen.

  2. Änderungen an der Datenbankverbindungszeichenfolge nehmen Sie in den Dateien Login.cs und NewUser.cs vor. Suchen Sie in diesen Dateien die nachstehende Zeile:

    private String strConnString = "Initial Catalog=ChatServer;Data Source=JIM\
    etSDK;Integrated Security=SSPI;";
    

Nehmen Sie die erforderlichen Änderungen an der Verbindungszeichenfolge so vor, dass diese auf Ihren Datenbankserver und Ihre neue Datenbank verweist, und speichern Sie die Dateien.

  1. In den Dateien NewUser.cs und Chat.cs müssen Sie in Abhängigkeit von Ihrer MSMQ-Installation Änderungen am Message Queue-Server vornehmen (Informationen zur MSMQ-Installation finden Sie in der Windows-Dokumentation). Suchen Sie in diesen beiden Dateien die folgende Zeile:

    private String strQueueServer = ".\\PRIVATE$\\";
    

Ändern Sie diese Zeile so, dass sie auf Ihren Message Queue-Server verweist, und speichern Sie auch diese Dateien. Wenn Sie den lokalen Computer verwenden, ändern Sie die Zeile nicht. Wenn Sie einen Message Queue-Server in Ihrer Domäne verwenden, ändern Sie diese Zeile in:

  private String strQueueServer = "SRVR\\";
  

So wird davon ausgegangen, dass SRVR der Name Ihres Message Queue-Servers ist.

  1. Führen Sie nun den folgenden Befehl über die Eingabeaufforderung aus:
csc /out:MQChat.exe Splash.cs Login.cs NewUser.cs Chat.cs

Hiermit wird die neue Datei MQChat.exe im gleichen Verzeichnis gespeichert, in dem sich auch die Quellcodedateien befinden. Führen Sie nun die Anwendung aus, und geben Sie zwei Benutzer an. Melden Sie sich mit dem Konto eines der Benutzer an, öffnen Sie dann eine weitere Instanz von MQChat, und melden Sie sich mit dem Konto des anderen Benutzers an. Versuchen Sie, Nachrichten an den jeweils anderen Benutzer zu senden.

Einschränkungen und zukünftige Erweiterungen

Ich habe MQChat entwickelt, um einmal die Unterstützung und die nahtlose Integration zu erforschen, die .NET für MSMQ bietet. Sie können eine Reihe von Änderungen an der Anwendung vornehmen, also beispielsweise dafür sorgen, dass im Chatfenster nur die Namen der aktuell angemeldeten Benutzer angezeigt werden. Hierfür können Sie ggf. eine separate Warteschlange verwenden, in der jeder Benutzer bei der Anmeldung eine Meldung ablegt.

In Abhängigkeit hiervon können Sie dann das Kombinationsfeld laden und möglicherweise eine Meldung anzeigen. Des Weiteren wäre eine Funktion denkbar, die es dem Benutzer ermöglicht, sein Kennwort zu ändern. Als zusätzliche Erweiterung könnte eine Option zum Versenden von Dateien (als Nachrichtenanlage) zwischen Benutzern von MQChat integriert werden.

Bedenken Sie beim Einrichten von individuellen Warteschlangen für jeden Benutzer auch die eingeschränkte Skalierbarkeit, wenn die Anzahl der Benutzer größer wird. In diesem Fall empfiehlt es sich sicherlich, nur eine Warteschlange einzurichten und dafür zu sorgen, dass jede eingehende Nachricht über eingebettete Empfängerinformationen verfügt. Jeder Client prüft dann jede in der Warteschlange eingehende Nachricht, während die eigentliche Nachrichtenübermittlung nur an den gewünschten Empfänger erfolgt.

Zusammenfassung

In diesem Artikel wurden einige Vorteile von MSMQ erläutert, und es sollte gezeigt werden, wie Sie mit dem .NET Framework und den zugehörigen Klassenbibliotheken auf die Funktionen von MSMQ zugreifen können. Wir haben gesehen, wie mithilfe von MSMQ eine einfache Chatanwendung entwickelt werden kann, die sogar den Offline-Empfang von Nachrichten ermöglicht. Sie können also auch dann Nachrichten an einen Benutzer senden, wenn dieser nicht bei MQChat angemeldet ist - sobald der Benutzer sich anmeldet, empfängt er alle Nachrichten, die an ihn gesendet wurden.

Mit Windows XP hat Microsoft eine neue Version von MSMQ, Version 3.0, veröffentlicht. MSMQ 3.0 bietet gegenüber Version 2.0 mehrere Verbesserungen, wie Internet Messaging, programmgesteuerte Verwaltung und Bereitstellung, ein umfassend erweitertes Programmierungsmodell und nicht zuletzt auch Verbesserungen an den MSMQ-Auslösern. Eine Besprechung dieser Features würde den Rahmen dieses Artikels sprengen. Weitere Informationen bieten jedoch die angegebenen Links.