This sample demonstrates how to use the Windows Azure Service Bus and the Messaging Session functionality.

The sample shows simple senders and receivers communicating via a Service Bus queue. The SampleManager first prompts for service namespace credentials. These are used to authenticate with the Access Control service, and acquire an access token that proves to the Service Bus infrastructure that the client is authorized to access the queue. The SampleManager creates two queues, one requiring sessions and one that does not, then creates one or more senders and receivers. The sender(s) send messages with a range of SessionId values simulating different orders into the queues, first to the sessionless queue then to the queue requiring sessions. The receivers read orders until the sessionless queue is empty, then receives messages by session.

When messages are being received from the sessionless queue, the receivers will process the messages in the order they appear, regardless of the SessionId. When messages are being received from the session queue, the receivers will only receive messages that have the same SessionId until there are no more remaining, then will begin receiving messages for the next available session. The receivers also use Session.State to keep track of how much time has been spent processing messages for that session. This state is persisted for the session, so even if a receiver goes down while processing messages in a session (which you can simulate by closing one of the Session Receiver windows) the state will be available to the next receiver that picks up the session.

Prerequisites

If you haven't already done so, please read the release notes document that explains how to sign up for a Windows Azure account and how to configure your environment.

SampleManager

The SampleManager gets user credentials and creates a ServiceBusNamespaceClient (namespaceClient). This entity holds the credentials and is used for all messaging management operations. The namespaceClient is used to create queues for communication between the sender(s) and receiver(s). The SampleManager then creates sender(s) and receiver(s), passing the user credentials to each.

The following code prompts for the issuer credentials and then constructs the listening URI using that information. The static ServiceBusEnvironment.CreateServiceUri function is provided to help construct the URI with the correct format and domain name. It is strongly recommended that you use this function instead of building the URI from scratch because the URI construction logic and format might change in future releases.

C# 
                        static void Main(string[] args)
                        {
                            // Setup:
                            ParseArgs(args);
                            GetUserCredentials();                        
                            CreateNamespaceClient();

                            // Create queues:
                            Console.WriteLine("Creating Queues...");
                            Queue sessionlessQueue = CreateQueue(false);
                            Console.WriteLine("Created {0}, Queue.RequiresSession = false", sessionlessQueue.Path);
                            Queue sessionQueue = CreateQueue(true);
                            Console.WriteLine("Created {0}, Queue.RequiresSession = true", sessionQueue.Path);

                            ...
                        }

                        static void CreateNamespaceClient()
                        {
                            TransportClientCredentialBase credentials = TransportClientCredentialBase.CreateSharedSecretCredential(
                                ServiceBusIssuerName, ServiceBusIssuerKey);

                            Uri managementUri = ServiceBusEnvironment.CreateServiceUri("https", ServiceBusNamespace, string.Empty);
                            namespaceClient = new ServiceBusNamespaceClient(managementUri, credentials);
                        }

                        static Queue CreateQueue(bool session)
                        {
                            QueueDescription queueDescription = new QueueDescription() { RequiresSession = session };
                            string queueName = (session ? sessionQueueName : sessionlessQueueName);

                            // Delete the queue if already exists before creation. 
                            if (namespaceManager.QueueExists(queueName))
                            {
                                namespaceManager.DeleteQueue(queueName);
                            }

                            return namespaceClient.CreateQueue(queueName, queueDescription);
                        }
                      

 The SampleManager then creates sender and receiver processes and starts them.

C# 
                        static void Main(string[] args)
                        {
                            ...

                                    // Start senders and receivers:
                                    Console.WriteLine("\nLaunching senders and receivers...");
                                    StartSenders();
                                    StartReceivers();

                                    Console.WriteLine("\nPress [Enter] to exit.");
                                    Console.ReadLine();

                            ...
                        }

                        private static void StartSenders()
                        {
                            ProcessStartInfo startInfo = new ProcessStartInfo();
                            startInfo.FileName = "SessionMessagesSampleSender.exe";
                            startInfo.Arguments = CreateArgs();
                            for (int i = 0; i < numSenders; ++i)
                            {
                                Process process = Process.Start(startInfo);
                                senderProcs.Add(process);
                            }
                            Thread.Sleep(500);
                            ArrangeWindows();
                        }

                        static void StartReceivers()
                        {
                            ProcessStartInfo startInfo = new ProcessStartInfo();
                            startInfo.FileName = "SessionMessagesSampleReceiver.exe";
                            startInfo.Arguments = CreateArgs();
                            for (int i = 0; i < numReceivers; ++i)
                            {
                                Process process = Process.Start(startInfo);
                                receiverProcs.Add(process);
                            }
                            Thread.Sleep(500);
                            ArrangeWindows();
                        }
                      

Finally, the SampleManager cleans up by deleting the queues and stopping the senders and receivers if necessary.

C# 
                    static void Main(string[] args)
                    {
                        ...

                    // Cleanup:
                    namespaceClient.DeleteQueue(sessionlessQueue.Path);
                    namespaceClient.DeleteQueue(sessionQueue.Path);
                    StopSenders();
                    StopReceivers();  
                    }

                    private static void StopSenders()
                    {
                        foreach (Process proc in senderProcs)
                        {
                            proc.CloseMainWindow();
                        }
                    }

                    static void StopReceivers()
                    {
                        foreach (Process proc in receiverProcs)
                        {
                            proc.CloseMainWindow();
                        }
                    }                        
Visual Basic 
                    

Sender

The sender gets user credentials as parameters from SampleManager and creates a MessagingFactory (messagingFactory). This entity holds the credentials and is used for all messaging runtime operations, such as opening a client to send messages to the queues. The sender then creates a client to the sessionless queue and sends messages.

C# 
                        static void Main(string[] args)
                        {
                            ParseArgs(args);
                            Console.Title = "MessageSender";

                            // Send messages to queue which does not require session
                            QueueClient queueClient = CreateQueueClient(SampleManager.SessionlessQueueName);
                            Console.WriteLine("Preparing to send messages to {0}...", queueClient.Path);
                            Thread.Sleep(3000);

                            SendMessages(queueClient);

                            ...
                        }

                        static void SendMessages(QueueClient queueClient)
                        {
                            // Send messages to queue:
                            Console.WriteLine("Sending messages to queue {0}", queueClient.Path);
                            using (MessageSender sender = queueClient.CreateSender())
                            {
                                System.Random rand = new Random();
                                for (int i = 0; i < SampleManager.NumMessages; ++i)
                                {
                                    string sessionName = rand.Next(SampleManager.NumSessions).ToString();
                                    BrokeredMessage message = CreateSessionMessage(sessionName);
                                    sender.Send(message);
                                    SampleManager.OutputMessageInfo("SEND: ", message);
                                    Thread.Sleep(senderDelay);
                                }
                            }
                            Console.WriteLine();
                        }

                        // Create the runtime entities (queue client)
                        static QueueClient CreateQueueClient(string queueName)
                        {
                            TransportClientCredentialBase credentials = TransportClientCredentialBase.CreateSharedSecretCredential(
                                ServiceBusIssuerName, ServiceBusIssuerKey);

                            Uri runtimeUri = ServiceBusEnvironment.CreateServiceUri("sb", ServiceBusNamespace, string.Empty);
                            MessagingFactory messagingFactory = MessagingFactory.Create(runtimeUri, credentials);

                            return messagingFactory.CreateQueueClient(queueName);
                        }

                        static BrokeredMessage CreateSessionMessage(string sessionId)
                        {
                            BrokeredMessage message = BrokeredMessage.CreateMessage();
                            message.SessionId = sessionId;
                            message.MessageId = "Order_" + Guid.NewGuid().ToString().Substring(0,5);
                            return message;
                        }
                      

 The sender then creates a client to the session queue and sends messages. Note that the messages are created with a SessionId regardless of which queue they are sent to.

C# 
                        static void Main(string[] args)
                        {
                            ...

                            // Send messages to queue requiring session
                            queueClient = CreateQueueClient(SampleManager.SessionQueueName);
                            Console.WriteLine("Preparing to send messages to {0}...", queueClient.Path);
                            Console.WriteLine("Press [Enter] to begin sending messages.");
                            Console.ReadLine();

                            SendMessages(queueClient);

                            // All messages sent
                            Console.WriteLine("\nSender complete.");
                            Console.ReadLine();
                        }                      
                      

Receiver

The receiver also gets user credentials as parameters from SampleManager and creates a MessagingFactory (messagingFactory). This entity holds the credentials and is used for all messaging runtime operations, such as opening a client to receive messages from the queues. The receiver then creates a client to the sessionless queue and starts receiving messages:

C# 
                        static void Main(string[] args)
                        {
                            ParseArgs(args);
            
                            // Create MessageReceiver for queue which does not require session
                            Console.Title = "MessageReceiver";
                            QueueClient queueClient = CreateQueueClient(SampleManager.SessionlessQueueName);
                            Console.WriteLine("Ready to receive messages from {0}...", queueClient.Path);

                            MessageReceiver receiver = queueClient.CreateReceiver(ReceiveMode.ReceiveAndDelete);
                            lastReceive = DateTime.Now;
                            ReceiveMessages(receiver);
                        
                            ...
                        }

                        static QueueClient CreateQueueClient(string queueName)
                        {
                            TransportClientCredentialBase credentials = TransportClientCredentialBase.CreateSharedSecretCredential(
                                ServiceBusIssuerName, ServiceBusIssuerKey);

                            Uri runtimeUri = ServiceBusEnvironment.CreateServiceUri("sb", ServiceBusNamespace, string.Empty);
                            MessagingFactory messagingFactory = MessagingFactory.Create(runtimeUri, credentials);

                            return messagingFactory.CreateQueueClient(queueName);
                        }

                        static void ReceiveMessages(MessageReceiver receiver)
                        {
                            // Read messages from queue until queue is empty:
                            Console.WriteLine("Reading messages from queue {0}", receiver.Path);
                            Console.WriteLine("Receiver Type: " + receiver.GetType().Name);
            
                            BrokeredMessage receivedMessage;
                            while (receiver.TryReceive(TimeSpan.FromSeconds(receiveMessageTimeout), out receivedMessage))
                            {
                                ProcessMessage(receivedMessage);
                            }

                            receiver.Close();
                        }

                        static void ProcessMessage(BrokeredMessage message, Session session = null)
                        {
                            DateTime startProcessingNewMessage = DateTime.Now;
                            TimeSpan elapsed = startProcessingNewMessage - lastReceive;
                            lastReceive = startProcessingNewMessage;


                            // Using the Session State to track how much processing time was spent on a group.
                            // This value will persist even if a receiver process is killed and the remaining 
                            // messages are picked up by another receiver.
                            string readState = null;
                            if (session != null)
                            {
                                TimeSpan totalElapsed = elapsed;

                                readState = GetState(session);
                                if (readState != null)
                                {
                                    TimeSpan prevElapsed = TimeSpan.FromSeconds(Double.Parse(readState));
                                    totalElapsed = elapsed + prevElapsed;
                                }
                                SetState(session, totalElapsed.TotalSeconds.ToString());                
                            }

                            SampleManager.OutputMessageInfo("RECV: ", message, "State: " + readState);

                            Thread.Sleep(receiverDelay);
                        }
                      

Once the sessionless queue is empty, the receiver creates a queue client for the session queue and calls AcceptSessionReceiver to begin receiving messages from the first available session. Once all messages from that session have been received, the receiver continues to call AcceptSessionReceiver until there are no sessions of messages remaining.

In ProcessMessage, the receiver makes use of the session state to add the processing time for the session each time a message is processed.

C# 
                    static void Main(string[] args)
                    {
                        ...
                        
                        // Create SessionReceiver for queue requiring session
                        Console.Title = "SessionReceiver";
                        QueueClient sessionQueueClient = CreateQueueClient(SampleManager.SessionQueueName);
                        Console.Clear();
                        Console.WriteLine("Ready to receive messages from {0}...", sessionQueueClient.Path);

                        bool allSessionsAccepted = false;
                        while (!allSessionsAccepted)
                        {
                            try
                            {
                                Console.WriteLine("Checking for session...");
                                SessionReceiver sessionReceiver = sessionQueueClient.AcceptSessionReceiver(ReceiveMode.ReceiveAndDelete, TimeSpan.FromSeconds(acceptSessionReceiverTimeout));
                                ReceiveSessionMessages(sessionReceiver);
                                Console.WriteLine("All received on this session");
                            }
                            catch (TimeoutException)
                            {
                                Console.WriteLine("Got TimeoutException, no more sessions available");
                                allSessionsAccepted = true;
                            }
                        }

                        Console.WriteLine("\nReceiver complete.");
                        Console.ReadLine();
                    }

                    static void ReceiveSessionMessages(SessionReceiver receiver)
                    {
                        // Read messages from queue until queue is empty:
                        Console.WriteLine("Reading messages from queue {0}", receiver.Path);
                        Console.WriteLine("Receiver Type:" + receiver.GetType().Name);
                        Console.WriteLine("Receiver.SessionId = " + receiver.SessionId);

                        BrokeredMessage receivedMessage;
                        while (receiver.TryReceive(TimeSpan.FromSeconds(receiveSessionMessageTimeout), out receivedMessage))
                        {
                            Session session = receiver.Session;
                            ProcessMessage(receivedMessage, session);
                        }

                        receiver.Close();
                    }                    
                    

Running the Sample

To run the sample, build the solution in Visual Studio or from the command line, then run the resulting SampleManager.exe executable file. The program will prompt for your service namespace and the issuer credentials. For the issuer secret, be sure to use the "Default Issuer Key" value (typically "owner") from the portal, rather than one of the management keys.

Expected Output - SampleManager

                         Please provide the namespace to use:
                        <service namespace>
                        Please provide the Issuer name to use:
                        <issuer name>
                        Please provide the Issuer key to use:
                        <issuer key>

                        Creating Queues...
                        Created OrderQueue_NoSession, Queue.RequiresSession = false
                        Created OrderQueue_Session, Queue.RequiresSession = true

                        Launching senders and receivers...

                        Press [Enter] to exit.
                    

Expected Output - Sender

                         Preparing to send messages to OrderQueue_NoSession...
                        Sending messages to queue OrderQueue_NoSession
                        SEND: Order_5618c - Group 2.
                        SEND: Order_44753 - Group 0.
                        SEND: Order_de2f7 - Group 0.
                        SEND: Order_d154e - Group 3.
                        ...

                        Preparing to send messages to OrderQueue_Session...
                        Press [Enter] to begin sending messages.

                        Sending messages to queue OrderQueue_Session
                        SEND: Order_f2251 - Group 2.
                        SEND: Order_444c9 - Group 0.
                        SEND: Order_0e7f6 - Group 2.
                        SEND: Order_77c74 - Group 2.
                        ...

                        Sender complete.
                    

Expected Output - Receiver

                         Ready to receive messages from OrderQueue_NoSession...
                        Reading messages from queue OrderQueue_NoSession
                        Receiver Type: SbmpMessageReceiver
                        RECV: Order_5618c - Group 2. State:
                        RECV: Order_44753 - Group 0. State:
                        RECV: Order_de2f7 - Group 0. State:
                        RECV: Order_d154e - Group 3. State:
                        ...

                        Ready to receive messages from OrderQueue_Session...
                        Checking for session...
                        Reading messages from queue ORDERQUEUE_SESSION
                        Receiver Type:SessionReceiver
                        Receiver.SessionId = 2
                        RECV: Order_f2251 - Group 2. State:
                        RECV: Order_0e7f6 - Group 2. State: 10.3260325
                        RECV: Order_77c74 - Group 2. State: 10.5930267
                        RECV: Order_43a8a - Group 2. State: 10.8810288

                        ...

                        RECV: Order_9d1f6 - Group 3. State: 17.7360284
                        RECV: Order_4d3ec - Group 3. State: 18.0000264
                        RECV: Order_501d2 - Group 3. State: 18.2650265
                        RECV: Order_5668b - Group 3. State: 18.5360271

                        All received on this session
                        Checking for session...
                        Got TimeoutException, no more sessions available

                        Receiver complete.                    
                    


Did you find this information useful? Please send your suggestions and comments about the documentation.