This sample demonstrates how to use the Windows Azure Service Bus and the Request/Response functionality.

The sample shows simple clients and servers communicating via two Service Bus queues. 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 for requests and one for responses. It then creates one or more clients and servers. Each client sends messages on the request queue and asynchronously receives a message on the response queue. The servers read a message from the request queue and sends a message on the response queue.

Many application scenarios involve two-way communications in which a sender would like to receive and correlate responses for the messages that it sends. This is supported in Service Bus through the use of the properties SessionId and ReplyToSessionId on a message. Consider a pattern involving several clients that are sending messages and expect responses for these messages to be directed back to them. To enable this pattern, a given client, say client “ABC”, would set ReplyToSessionId=”ABC” on any messages it sends and also use MessageSession on a reply Topic/Subscription or Queue to listen for messages where SessionId=ABC. To complete the pattern, any processor of the message would set SessionId=ReplyToSessionId once a message has been processed.

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 NamespaceManager (namespaceManager). This entity holds the credentials and is used for all messaging management operations. The namespaceClient is used to create queues for communication between the client(s) and server(s). The SampleManager then creates client(s) and server(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("\nCreating Queues...");
                            QueueDescription requestQueue = CreateQueue(requestQueuePath, false);
                            Console.WriteLine(
                                "Created {0}, Queue.RequiresSession = {1}", requestQueue.Path, requestQueue.RequiresSession);
                            QueueDescription responseQueue = CreateQueue(responseQueuePath, true);
                            Console.WriteLine(
                                "Created {0}, Queue.RequiresSession = {1}", responseQueue.Path, responseQueue.RequiresSession);

                            ...
                        }

                        // Create the management entities (queue)
                        static void CreateNamespaceClient()
                        {
                            TokenProvider tokenProvider = TokenProvider.CreateSharedSecretTokenProvider(
                                ServiceBusIssuerName, ServiceBusIssuerKey);

                            Uri uri = ServiceBusEnvironment.CreateServiceUri("sb", ServiceBusNamespace, string.Empty);
                            namespaceManager = new NamespaceManager(uri, tokenProvider);
                        }

                        static QueueDescription CreateQueue(string queuePath, bool session)
                        {
                            QueueDescription queueDescription = new QueueDescription() { RequiresSession = session, Path = queuePath};

                            // Delete the queue if it already exists
                            if (namespaceManager.QueueExists(queuePath))
                            {
                                namespaceManager.DeleteQueue(queuePath);
                            }

                            return namespaceManager.CreateQueue(queueDescription);
                        }
                      

 The SampleManager then creates client and server processes and starts them.

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

                            // Start clients and servers:
                            Console.WriteLine("\nLaunching clients and servers...");
                            StartClients();
                            StartServers();

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

                            ...
                        }

                        static void StartClients()
                        {
                            ProcessStartInfo startInfo = new ProcessStartInfo();
                            startInfo.FileName = "RequestResponseSampleClient.exe";
                            for (int i = 0; i < numClients; ++i)
                            {
                                startInfo.Arguments = CreateArgs(i.ToString());
                                Process process = Process.Start(startInfo);
                                clientProcs.Add(process);
                            }
                            Thread.Sleep(500);
                            ArrangeWindows();
                        }

                        static void StartServers()
                        {
                            ProcessStartInfo startInfo = new ProcessStartInfo();
                            startInfo.FileName = "RequestResponseSampleServer.exe";
                            startInfo.Arguments = CreateArgs();
                            for (int i = 0; i < numServers; ++i)
                            {
                                Process process = Process.Start(startInfo);
                                serverProcs.Add(process);
                            }
                            Thread.Sleep(500);
                            ArrangeWindows();
                        }
                      

Finally, the SampleManager cleans up by deleting the queues and stopping the clients and servers if necessary.

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

                        // Cleanup:
                        namespaceClient.DeleteQueue(requestQueue.Path);
                        namespaceClient.DeleteQueue(responseQueue.Path);
                        StopClients();
                        StopServers();  
                    }

                    static void StopClients()
                    {
                        foreach (Process proc in clientProcs)
                        {
                            proc.CloseMainWindow();
                        }
                    }

                    static void StopServers()
                    {
                        foreach (Process proc in serverProcs)
                        {
                            proc.CloseMainWindow();
                        }
                    }                        
                    

Client

The Client 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 QueueClient to send messages to the queues. The Client then creates QueueClients to the request and response queues. Messages are sent to the server on the request queue and responses are processed asynchonously by calling responseClient.BeginReceive().

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

                            // Send request messages to request queue
                            QueueClient requestClient = CreateQueueClient(SampleManager.RequestQueuePath);
                            QueueClient responseClient = CreateQueueClient(SampleManager.ResponseQueuePath);
                            MessageSession session = responseClient.AcceptMessageSession(ClientId);
                            Console.WriteLine("Preparing to send request messages to {0}...", requestClient.Path);

                            SendMessages(requestClient, responseClient, session);

                            ...
                        }

                        // Create the runtime entities (queue client)
                        static QueueClient CreateQueueClient(string queuePath)
                        {
                            TokenProvider tokenProvider = TokenProvider.CreateSharedSecretTokenProvider(
                                ServiceBusIssuerName, ServiceBusIssuerKey);

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

                            return messagingFactory.CreateQueueClient(queuePath, ReceiveMode.ReceiveAndDelete);
                        }

                        static void SendMessages(QueueClient requestClient, QueueClient responseClient, MessageSession session)
                        {
                            // Send request messages to queue:
                            Console.WriteLine("Sending request messages to queue {0}", requestClient.Path);
                            Console.WriteLine("Receiving response messages on queue {0}", responseClient.Path);

                            for (int i = 0; i < SampleManager.NumMessages; ++i)
                            {
                                // send request message
                                BrokeredMessage message = new BrokeredMessage { ReplyToSessionId = ClientId, MessageId = i.ToString() };
                
                                requestClient.Send(message);
                                SampleManager.OutputMessageInfo("REQUEST: ", message);

                                // start asynchronous receive operation
                                session.BeginReceive(TimeSpan.FromSeconds(ResponseMessageTimeout), ProcessResponse, session);
                            }


                            Console.WriteLine();
                        }

                        static void ProcessResponse(IAsyncResult result)
                        {
                            MessageSession session = result.AsyncState as MessageSession;
                            BrokeredMessage message = session.EndReceive(result);

                            if (message == null)
                            {
                                Console.WriteLine("ERROR: Message Receive Timeout.");
                            }
                            else
                            {
                                SampleManager.OutputMessageInfo("RESPONSE: ", message);
                            }
                        }
                      

Server

The Server 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 QueueClient to receive messages from the queues. The Server then creates QueueClients to the request and response queues and starts receiving and responding to request messages:

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

                            // Receive request messages from request queue
                            Console.Title = "Server";
                            QueueClient requestClient = CreateQueueClient(SampleManager.RequestQueuePath);
                            QueueClient responseClient = CreateQueueClient(SampleManager.ResponseQueuePath);
                            Console.WriteLine("Ready to receive messages from {0}...", requestClient.Path);

                            ReceiveMessages(requestClient, responseClient);
                        
                            ...
                        }

                        static QueueClient CreateQueueClient(string queuePath)
                        {
                            TokenProvider tokenProvider = TokenProvider.CreateSharedSecretTokenProvider(
                                ServiceBusIssuerName, ServiceBusIssuerKey);

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

                            return messagingFactory.CreateQueueClient(queuePath, ReceiveMode.ReceiveAndDelete);
                        }

                        static void ReceiveMessages(QueueClient requestClient, QueueClient responseClient)
                        {
                            // Read messages from queue until queue is empty:
                            Console.WriteLine("Reading messages from queue {0}", requestClient.Path);

                            BrokeredMessage request;
                            while ((request = requestClient.Receive(TimeSpan.FromSeconds(ReceiveMessageTimeout))) != null)
                            {
                                SampleManager.OutputMessageInfo("REQUEST: ", request);

                                BrokeredMessage response = new BrokeredMessage
                                    { SessionId = request.ReplyToSessionId, MessageId = request.MessageId };

                                responseClient.Send(response);
                                SampleManager.OutputMessageInfo("RESPONSE: ", response);
                            }
                        }
                      

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 RequestQueue, Queue.RequiresSession = False
                        Created ResponseQueue, Queue.RequiresSession = True

                        Launching clients and servers...

                        Press [Enter] to exit.
                    

Expected Output - Client

                        Preparing to send request messages to RequestQueue...
                        Sending request messages to queue RequestQueue
                        Receiving response messages on queue ResponseQueue
                        REQUEST: 0 - Client 0.
                        REQUEST: 1 - Client 0.
                        REQUEST: 2 - Client 0.
                        REQUEST: 3 - Client 0.
                        REQUEST: 4 - Client 0.
                        REQUEST: 5 - Client 0.
                        REQUEST: 6 - Client 0.
                        REQUEST: 7 - Client 0.
                        REQUEST: 8 - Client 0.
                        REQUEST: 9 - Client 0.


                        Client finished sending requests.
                        RESPONSE: 0 - Client 0.
                        RESPONSE: 1 - Client 0.
                        RESPONSE: 2 - Client 0.
                        RESPONSE: 3 - Client 0.
                        RESPONSE: 4 - Client 0.
                        RESPONSE: 5 - Client 0.
                        RESPONSE: 6 - Client 0.
                        RESPONSE: 7 - Client 0.
                        RESPONSE: 8 - Client 0.
                        RESPONSE: 9 - Client 0.
                    

Expected Output - Server

                        Ready to receive messages from RequestQueue...
                        Reading messages from queue RequestQueue
                        REQUEST: 0 - Client 2.
                        RESPONSE: 0 - Client 2.
                        REQUEST: 0 - Client 0.
                        RESPONSE: 0 - Client 0.
                        REQUEST: 0 - Client 1.
                        RESPONSE: 0 - Client 1.
                        REQUEST: 0 - Client 3.
                        RESPONSE: 0 - Client 3.
                        REQUEST: 1 - Client 2.
                        RESPONSE: 1 - Client 2.
                        REQUEST: 1 - Client 0.
                        RESPONSE: 1 - Client 0.
                        REQUEST: 1 - Client 1.
                        RESPONSE: 1 - Client 1.
                        REQUEST: 1 - Client 3.
                        RESPONSE: 1 - Client 3.
                        REQUEST: 2 - Client 2.
                        RESPONSE: 2 - Client 2.
                        REQUEST: 2 - Client 3.
                        RESPONSE: 2 - Client 3.
                        REQUEST: 2 - Client 1.
                        RESPONSE: 2 - Client 1.
                        REQUEST: 2 - Client 0.
                        RESPONSE: 2 - Client 0.
                        
                        ...

                        REQUEST: 8 - Client 0.
                        RESPONSE: 8 - Client 0.
                        REQUEST: 8 - Client 1.
                        RESPONSE: 8 - Client 1.
                        REQUEST: 8 - Client 2.
                        RESPONSE: 8 - Client 2.
                        REQUEST: 8 - Client 3.
                        RESPONSE: 8 - Client 3.
                        REQUEST: 9 - Client 0.
                        RESPONSE: 9 - Client 0.
                        REQUEST: 9 - Client 1.
                        RESPONSE: 9 - Client 1.
                        REQUEST: 9 - Client 3.
                        RESPONSE: 9 - Client 3.
                        REQUEST: 9 - Client 2.
                        RESPONSE: 9 - Client 2.

                        Server complete.