Introduction
The article describes a simple XMPP client application based on the Windows Workflow Foundation, which allows to send and receive messages and also to add and remove contacts from your contact list.
Background
Extensible Messaging and Presence Protocol
The Extensible Messaging and Presence Protocol (XMPP) is an open technology for real time communication, using Extensible Markup Language (XML) as the base format for exchanging information. In essence, XMPP provides a way to send small pieces of XML from one entity to another in close to real time.
The XMPP protocol is taken as the RFC standard, and is described by the following documents:
- RFC 3920 Extensible Messaging and Presence Protocol: Core
- RFC 3921 Extensible Messaging and Presence Protocol: Instant Messaging and Presence
Windows Workflow Foundation
Windows Workflow Foundation (WF) is a Microsoft technology for defining, executing, and managing workflows. This technology was first released in November 2006 as a part of .NET Framework 3.0.
WF is a special runtime environment that manages the execution of programs that are composed of resumable program statements. In WF, a resumable program statement is called an Activity. A resumable program is a composition of activities. In WF, such a program is often called a Workflow.
Using the Code
The project consists of two libraries and an XOML-file: JabberClient.dll (root namespace is Jabber.Client
), WorkflowActivities.dll (root namespace is Workflow
), and Sequence.xml.
Namespace Jabber.Client
The Jabber.Client
namespace includes the types for the XMPP client realization with minimal functionality, which are presented in the following list:
Jabber.Client.Core.TcpBinder
wraps the standard System.Net.Sockets.Socket
class.Jabber.Client.Core.ClientState
enumerates the possible client states.Jabber.Client.Xml.PacketBuilder
contains the methods for building XML-strings for XMPP-server requests (authentication, contact list request, message sending, etc.).Jabber.Client.Xml.PacketParser
contains the methods for XMPP-server response parsing.Jabber.Client.Cryptography.DigestMD5Auth
implies digest authentication according to RFC2831.Jabber.Client.JabberClient
encapsulates all of XMPP-client logic.
Namespace Worflow
The Worflow
namespace includes the set of activities and helper classes which help to specify the program algorithm. You can see the list of main activities below.
Workflow.Activities.Receiver
retrieves the message from a Queue
, converts it with the help of the ReceiveMethod
method of the ReceiveType
class of the AssemblyFile
assembly, and calls the OnReceived
method of the IWorkflowDataService
interface.Workflow.Activities.Sender
retrieves the message from a Queue
, converts it with the help of the SendMethod
method of the SendType
class of the AssemblyFile
assembly, and calls the Send
method of the IWorkflowDataService
interface.Workflow.Activities.StateSendAndReceive
sends the message and receives the response. For it to work, the activity needs these properties: ReceiveMethod
, ReceiveType
, AssemblyFile
, SendMethod
, SendType
, and Context
. The Context
property is used as in-out params.Workflow.Activities.Sequence
is a type of CompositeActivity
, which sequentially executes its child activities.Workflow.Activities.Switch
emulates a switch
instruction.
Thus, the simple XMPP client's XOML-file is represented below:
<Sequence x:Name="root"
xmlns="http://Workflow/Activities"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wf="http://schemas.microsoft.com/winfx/2006/xaml/workflow">
<Switch x:Name="sw1">
<StateSequence x:Name="Auth" Queue="AuthInputQueue">
<StateSendAndReceive Name="step1_0" AssemblyFile="JabberClient.dll"
SendType="Jabber.Client.Xml.PacketBuilder" SendMethod="AuthStreamHeader"
ReceiveType="Jabber.Client.Xml.PacketParser" ReceiveMethod="AuthStreamHeader1"
Context="{wf:ActivityBind Auth, Path=Context}" />
<StateSendAndReceive Name="step1_1" AssemblyFile="JabberClient.dll"
ReceiveType="Jabber.Client.Xml.PacketParser"
ReceiveMethod="AuthStreamHeader2"
Context="{wf:ActivityBind step1_0, Path=Context}" />
........
</StateSequence>
<Receiver x:Name="Receive" Queue="ReceiveQueue"
AssemblyFile="JabberClient.dll"
ReceiveType="Jabber.Client.Xml.PacketParser"
ReceiveMethod="ResponseMessage" />
<Sender x:Name="Send" Queue="SendQueue"
AssemblyFile="JabberClient.dll"
SendType="Jabber.Client.Xml.PacketBuilder"
SendMethod="ChatMessage"/>
<Sender x:Name="Status" Queue="StatusQueue"
AssemblyFile="JabberClient.dll"
SendType="Jabber.Client.Xml.PacketBuilder"
SendMethod="PresenceShow"/>
<Sender x:Name="Roster" Queue="RosterQueue"
AssemblyFile="JabberClient.dll"
SendType="Jabber.Client.Xml.PacketBuilder"
SendMethod="Roster"/>
<Sender x:Name="Subscribe" Queue="RosterQueue"
AssemblyFile="JabberClient.dll"
SendType="Jabber.Client.Xml.PacketBuilder"
SendMethod="Subscribe"/>
<Sender x:Name="Unsubscribe" Queue="RosterQueue"
AssemblyFile="JabberClient.dll"
SendType="Jabber.Client.Xml.PacketBuilder"
SendMethod="Unsubscribe"/>
</Switch>
</Sequence>
It follows from the XOML-file that the StateSequence
activity performs the client authentication on the XMPP-server according to RFC2831. Dynamic binding of the Context
property is used in child Activities.
An example of using a simple XMPP-client is shown below:
....
using Jabber.Client;
using Jabber.Client.Core;
....
using (JabberClient jabber =
new JabberClient("jabber.org", "vba32cc", "1234qwer"))
{
jabber.Authentificated += new EventHandler(jabber_Authentificated);
jabber.RosterReceived += new RosterHandler(jabber_RosterReceived);
jabber.MessageReceived += new MessageHandler(jabber_MessageReceived);
Console.WriteLine("Authentification...");
jabber.Auth();
string s = Console.ReadLine();
Console.WriteLine("Client state={0}", jabber.State);
while (!string.IsNullOrEmpty(s))
{
if (jabber.State == ClientState.Authentificated)
{
Contact contact = new Contact("", "veleslaff@jabber.ru");
jabber.AddToContacts(contact);
jabber.SetStatusForContact(contact, UserStatus.Online);
jabber.Send(contact, s);
jabber.Contacts();
}
s = Console.ReadLine();
}
}
....
Points of Interest
The error handling in the workflow library and the host program's notification need improving. Classes in the Jabber.Client.Xml
namespace need refactoring.
History
- 27th December, 2009: First version of a simple XMPP-client with basic functionality.
- 28th December, 2009: Updated the sample project.