diff options
Diffstat (limited to 'qpid/doc/book/src/NET-User-Guide.xml')
-rw-r--r-- | qpid/doc/book/src/NET-User-Guide.xml | 1383 |
1 files changed, 1383 insertions, 0 deletions
diff --git a/qpid/doc/book/src/NET-User-Guide.xml b/qpid/doc/book/src/NET-User-Guide.xml new file mode 100644 index 0000000000..7bfa20b8c8 --- /dev/null +++ b/qpid/doc/book/src/NET-User-Guide.xml @@ -0,0 +1,1383 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + +--> + +<section> + <title> + Apache Qpid: Open Source AMQP Messaging - .NET User Guide + </title> + <section role="h1" id="NETUserGuide-Tutorial"> + <title> + Tutorial + </title> + <para> + This tutorial consists of a series of examples using the three + most commonly used exchange types - Direct, Fanout and + Topic + exchanges. These examples show how to write applications that use + the most common messaging paradigms. + </para> + <itemizedlist> + <listitem> + <para>direct</para> + <para>In the direct examples, a message producer writes to the direct + exchange, specifying a routing key. A message consumer reads + messages from a named queue. This illustrates clean separation + of concerns - message producers need to know only the exchange + and the routing key, message consumers need to know only which + queue to use on the broker. + </para> + </listitem> + <listitem> + <para>fanout</para> + <para>The fanout examples use a fanout exchange and do not use + routing keys. Each binding specifies that all messages for a + given exchange should be delivered to a given queue. + </para> + </listitem> + <listitem> + <para>pub-sub</para> + <para>In the publish/subscribe examples, a publisher + application writes messages to an exchange, specifying a + multi-part key. A subscriber application subscribes to + messages that match the relevant parts of these keys, using a + private queue for each subscription. + </para> + </listitem> + <listitem> + <para>request-response</para> + <para>In the request/response examples, a simple service accepts + requests from clients and sends responses back to them. Clients + create their own private queues and corresponding routing keys. + When a client sends a request to the server, it specifies its + own routing key in the reply-to field of the request. The + server uses the client's reply-to field as the routing key for + the response. + </para> + </listitem> + </itemizedlist> + <section role="h2" id="NETUserGuide-RunningtheExamples"> + <title> + Running the + Examples + </title> + <para> + Before running the examples, you need to unzip the file + Qpid.NET-net-2.0-M4.zip, the following tree is created: + </para> + + + <programlisting> +<home> + |-qpid + |-lib (contains the required dlls) + |-examples + |- direct + | |-example-direct-Listener.exe + | |-example-direct-Producer.exe + |- fanout + | |-example-fanout-Listener.exe + | |-example-fanout-Producer.exe + |- pub-sub + | |-example-pub-sub-Listener.exe + | |-example-pub-sub-Publisher.exe + |- request-response + |-example-request-response-Client.exe + |-example-request-response-Server.exe + </programlisting> + + + <para> + Make sure your PATH contains the directory + <home>/qpid/lib + The examples can be run by executing the provided exe files: + </para> + + + <programlisting> +$ cd <home>/qpid/examples/examplefolder +$ example-...-.exe [hostname] [portnumber] + </programlisting> + + + <para> + where [hostname] is the qpid broker host name + (default is localhost) and [portnumber] is the port number on which the + qpid broker is accepting connection (default is 5672). + </para> + <!--h2--> + </section> + + <section role="h2" id="NETUserGuide-CreatingandClosingSessions"> + <title> + Creating + and Closing Sessions + </title> + + <para> + All of the examples have been written using the Apache Qpid .NEt + 0.10 API. The examples use the same skeleton code to initialize + the program, create a session, and clean up before exiting: + </para> + + + <programlisting> +using System; +using System.IO; +using System.Text; +using System.Threading; +using org.apache.qpid.client; +using org.apache.qpid.transport; + +... + + private static void Main(string[] args) + { + string host = args.Length > 0 ? args[0] : "localhost"; + int port = args.Length > 1 ? Convert.ToInt32(args[1]) : 5672; + Client connection = new Client(); + try + { + connection.connect(host, port, "test", "guest", "guest"); + ClientSession session = connection.createSession(50000); + + //--------- Main body of program -------------------------------------------- + + connection.close(); + } + catch (Exception e) + { + Console.WriteLine("Error: \n" + e.StackTrace); + } + } +... + </programlisting> + <!--h2--> + </section> + + <section role="h2" id="NETUserGuide-WritingDirectApplications"> + <title> + Writing + Direct Applications + </title> + + <para> + This section describes two programs that implement direct + messaging using a Direct exchange: + • org.apache.qpid.example.direct.Producer (from + example-direct-producer) publishes messages to the amq.direct + exchange, using the routing key routing_key. + •org.apache.qpid.example.direct.Listener (from + example-direct-Listener) uses a message listener to receive + messages from the queue named message_queue. + </para> + <section role="h3" id="NETUserGuide-RunningtheDirectExamples"> + <title> + Running the + Direct Examples + </title> + <para> + 1) Make sure your PATH contains the directory + <home>/qpid/lib + </para> + <para> + 2) Make sure that a qpid broker is running: + </para> + + + <programlisting> +$ ps -eaf | grep qpidd + </programlisting> + + + <para> + If a broker is running, you should see the qpidd process in the + output of the above + command. + </para> + <para> + 3) Read the messages from the message queue using direct + listener, as follows: + </para> + + + <programlisting> +$ cd <home>/qpid/examples/direct + </programlisting> + + + <para> + With cygwin: + </para> + + + <programlisting> +$ ./example-direct-Listener.exe [hostname] [portnumber] + </programlisting> + + + <para> + or with mono: + </para> + + + <programlisting> +$ mono ./example-direct-Listener.exe [hostname] [portnumber] + </programlisting> + + + <para> + This program is waiting for messages to be published, see next + step: + </para> + <para> + 4) Publish a series of messages to the amq.direct exchange by + running direct producer, as follows: + </para> + + + <programlisting> +$ cd <home>/qpid/examples/direct + </programlisting> + + + <para> + With cygwin: + </para> + + + <programlisting> +$ ./example-direct-Producer.exe [hostname] [portnumber] + </programlisting> + + + <para> + or with mono: + </para> + + + <programlisting> +$ mono ./example-direct-Producer.exe [hostname] [portnumber] + </programlisting> + + + <para> + This program has no output; the messages are routed to the + message queue, as instructed by the binding. + </para> + <para> + 5) Go to the windows where you are running your listener. You + should see the following output: + </para> + + + <programlisting> +Message: Message 0 +Message: Message 1 +Message: Message 2 +Message: Message 3 +Message: Message 4 +Message: Message 5 +Message: Message 6 +Message: Message 7 +Message: Message 8 +Message: Message 9 +Message: That's all, folks! + </programlisting> + + + <para> + Now we will examine the code for each of these programs. In each + section, we will discuss only + the code that must be added to the skeleton shown in Section + "Creating and Closing Sessions". + </para> + <!--h3--> + </section> + + <section role="h3" id="NETUserGuide-ReadingMessagesfromtheQueue"> + <title> + Reading + Messages from the Queue + </title> + + <para> + The program , listener.cs, is a message listener that receives + messages from a queue. + </para> + <para> + First it creates a queue named message_queue, then binds it to + the amq.direct exchange using the binding key routing_key. + </para> + + + <programlisting> +//--------- Main body of program -------------------------------------------- +// Create a queue named "message_queue", and route all messages whose +// routing key is "routing_key" to this newly created queue. +session.queueDeclare("message_queue"); +session.exchangeBind("message_queue", "amq.direct", "routing_key"); + </programlisting> + + + <para> + The queue created by this program continues to exist after the + program exits, and any message whose routing key matches the key + specified in the binding will be routed to the corresponding + queue by the broker. Note that the queue could have been be + deleted using the following code: + </para> + + + <programlisting> +session.queueDelete("message_queue"); + </programlisting> + + + <para> + To create a message listener, create a class derived from + IMessageListener, and override the messageTransfer method, + providing the code that should be executed when a message is + received. + </para> + + + <programlisting> +public class MessageListener : IMessageListener +{ + ...... + public void messageTransfer(IMessage m) + { + ..... +} + </programlisting> + + + <para> + The main body of the program creates a listener for the + subscription; attaches the listener to a message queue; and + subscribe to the queue to receive messages from the queue. + </para> + + + <programlisting> +lock (session) +{ + // Create a listener and subscribe it to the queue named "message_queue" + IMessageListener listener = new MessageListener(session); + session.attachMessageListener(listener, "message_queue"); + session.messageSubscribe("message_queue"); + // Receive messages until all messages are received + Monitor.Wait(session); +} + </programlisting> + + + <para> + The MessageListener's messageTransfer() function is called + whenever a message is received. In this example the message is + printed and tested to see if it is the final message. Once the + final message is received, the messages are acknowledged. + </para> + + + <programlisting> +BinaryReader reader = new BinaryReader(m.Body, Encoding.UTF8); +byte[] body = new byte[m.Body.Length - m.Body.Position]; +reader.Read(body, 0, body.Length); +ASCIIEncoding enc = new ASCIIEncoding(); +string message = enc.GetString(body); + Console.WriteLine("Message: " + message); +// Add this message to the list of message to be acknowledged +_range.add(m.Id); +if( message.Equals("That's all, folks!") ) +{ + // Acknowledge all the received messages + _session.messageAccept(_range); + lock(_session) + { + Monitor.Pulse(_session); + } +} + </programlisting> + <!--h3--> + </section> + + <section role="h3" id="NETUserGuide-PublishingMessagestoaDirectExchange"> + <title> + Publishing + Messages to a Direct Exchange + </title> + <para> + The second program in the direct example, Producer.cs, publishes + messages to the amq.direct exchange using the routing key + routing_key. + </para> + <para> + First, create a message and set a routing key. The same routing + key will be used for each message we send, so you only need to + set this property once. + </para> + + + <programlisting> +IMessage message = new Message(); +// The routing key is a message property. We will use the same +// routing key for each message, so we'll set this property +// just once. (In most simple cases, there is no need to set +// other message properties.) +message.DeliveryProperties.setRoutingKey("routing_key"); + </programlisting> + + + <para> + Now send some messages: + </para> + + + <programlisting> +// Asynchronous transfer sends messages as quickly as +// possible without waiting for confirmation. +for (int i = 0; i < 10; i++) +{ + message.clearData(); + message.appendData(Encoding.UTF8.GetBytes("Message " + i)); + session.messageTransfer("amq.direct", message); +} + </programlisting> + + + <para> + Send a final synchronous message to indicate termination: + </para> + + + <programlisting> +// And send a syncrhonous final message to indicate termination. +message.clearData(); +message.appendData(Encoding.UTF8.GetBytes("That's all, folks!")); +session.messageTransfer("amq.direct", "routing_key", message); +session.sync(); + </programlisting> + <!--h3--> + </section> + + <!--h2--> + </section> + + + <section role="h2" id="NETUserGuide-WritingFanoutApplications"> + <title> + Writing + Fanout Applications + </title> + + <para> + This section describes two programs that illustrate the use of a + Fanout exchange. + </para> + <itemizedlist> + <listitem> + <para>Listener.cs makes a unique queue private for each instance of + the listener, and binds that queue to the fanout exchange. All + messages sent to the fanout exchange are delivered to each + listener's queue. + </para> + </listitem> + <listitem> + <para>Producer.cs publishes messages to the fanout exchange. It + does not use a routing key, which is not needed by the fanout + exchange. + </para> + </listitem> + </itemizedlist> + <section role="h3" id="NETUserGuide-RunningtheFanoutExamples"> + <title> + Running the + Fanout Examples + </title> + + <para> + 1) Make sure your PATH contains the directory + <home>/qpid/lib + </para> + <para> + 2) Make sure that a qpid broker is running: + </para> + + + <programlisting> +$ ps -eaf | grep qpidd + </programlisting> + + + <para> + If a broker is running, you should see the qpidd process in the + output of the above + command. + </para> + <para> + 3) In separate windows, start one or more fanout listeners as + follows: + </para> + + + <programlisting> +$ cd <home>/qpid/examples/direct + </programlisting> + + + <para> + With cygwin: + </para> + + + <programlisting> +$ ./example-fanout-Listener.exe [hostname] [portnumber] + </programlisting> + + + <para> + or with mono: + </para> + + + <programlisting> +$ mono ./example-fanout-Listener.exe [hostname] [portnumber] + </programlisting> + + + <para> + The listener creates a private queue, binds it to the amq.fanout + exchange, and waits for messages to arrive on the queue. When the + listener starts, you will see the following message: + </para> + + + <programlisting> +Listening + </programlisting> + + + <para> + This program is waiting for messages to be published, see next + step: + </para> + <para> + 4) In a separate window, publish a series of messages to the + amq.fanout exchange by running fanout producer, as follows: + </para> + + + <programlisting> +$ cd <home>/qpid/examples/direct + </programlisting> + + + <para> + With cygwin: + </para> + + + <programlisting> +$ ./example-fanout-Producer.exe [hostname] [portnumber] + </programlisting> + + + <para> + or with mono: + </para> + + + <programlisting> +$ mono ./example-fanout-Producer.exe [hostname] [portnumber] + </programlisting> + + + <para> + This program has no output; the messages are routed to the + message queue, as prescribed by the binding. + </para> + <para> + 5) Go to the windows where you are running listeners. You should + see the following output for each listener: + </para> + + + <programlisting> +Message: Message 0 +Message: Message 1 +Message: Message 2 +Message: Message 3 +Message: Message 4 +Message: Message 5 +Message: Message 6 +Message: Message 7 +Message: Message 8 +Message: Message 9 +Message: That's all, folks! + </programlisting> + + + <para> + Now we will examine the code for each of these programs. In each + section, we will discuss only + the code that must be added to the skeleton shown in Section + "Creating and Closing Sessions". + </para> + + <!--h3--> + </section> + + <!--h2--> + </section> + + <section role="h2" id="NETUserGuide-ConsumingfromaFanoutExchange"> + <title> + Consuming from a + Fanout Exchange + </title> + + <para> + The first program in the fanout example, Listener.cs, creates a + private queue, binds it to the amq.fanout exchange, and waits for + messages to arrive on the queue, printing them out as they + arrive. It uses a Listener that is identical to the one used in + the direct example: + </para> + + + <programlisting> + public class MessageListener : IMessageListener + { + private readonly ClientSession _session; + private readonly RangeSet _range = new RangeSet(); + public MessageListener(ClientSession session) + { + _session = session; + } + + public void messageTransfer(IMessage m) + { + BinaryReader reader = new BinaryReader(m.Body, Encoding.UTF8); + byte[] body = new byte[m.Body.Length - m.Body.Position]; + reader.Read(body, 0, body.Length); + ASCIIEncoding enc = new ASCIIEncoding(); + string message = enc.GetString(body); + Console.WriteLine("Message: " + message); + // Add this message to the list of message to be acknowledged + _range.add(m.Id); + if (message.Equals("That's all, folks!")) + { + // Acknowledge all the received messages + _session.messageAccept(_range); + lock (_session) + { + Monitor.Pulse(_session); + } + } + } + } + </programlisting> + + + <para> + The listener creates a private queue to receive its messages and + binds it to the fanout exchange: + </para> + + + <programlisting> +string myQueue = session.Name; +session.queueDeclare(myQueue, Option.EXCLUSIVE, Option.AUTO_DELETE); +session.exchangeBind(myQueue, "amq.fanout", "my-key"); + </programlisting> + + + <para> + Now we create a listener and subscribe it to the queue: + </para> + + + <programlisting> +lock (session) +{ + Console.WriteLine("Listening"); + // Create a listener and subscribe it to my queue. + IMessageListener listener = new MessageListener(session); + session.attachMessageListener(listener, myQueue); + session.messageSubscribe(myQueue); + // Receive messages until all messages are received + Monitor.Wait(session); +} + </programlisting> + + + <section role="h3" id="NETUserGuide-PublishingMessagestotheFanoutExchange"> + <title> + Publishing + Messages to the Fanout Exchange + </title> + + <para> + The second program in this example, Producer.cs, writes messages + to the fanout queue. + </para> + + + <programlisting> +// Unlike topic exchanges and direct exchanges, a fanout +// exchange need not set a routing key. +IMessage message = new Message(); +// Asynchronous transfer sends messages as quickly as +// possible without waiting for confirmation. +for (int i = 0; i < 10; i++) +{ + message.clearData(); + message.appendData(Encoding.UTF8.GetBytes("Message " + i)); + session.messageTransfer("amq.fanout", message); +} + +// And send a syncrhonous final message to indicate termination. +message.clearData(); +message.appendData(Encoding.UTF8.GetBytes("That's all, folks!")); +session.messageTransfer("amq.fanout", message); +session.sync(); + </programlisting> + + <!--h3--> + </section> + + <!--h2--> + </section> + + <section role="h2" id="NETUserGuide-WritingPublish-2FSubscribeApplications"> + <title> + Writing + Publish/Subscribe Applications + </title> + + <para> + This section describes two programs that implement + Publish/Subscribe messaging using a topic exchange. + </para> + <para> + • Publisher.cS sends messages to the amq.topic exchange, + using the multipart routing keys usa.news, usa.weather, + europe.news, and europe.weather. + • Listener.cs creates private queues for news, weather, + usa, and europe, binding them to the amq.topic exchange using + bindings that match the corresponding parts of the multipart + routing keys. + </para> + <para> + In this example, the publisher creates messages for topics like + news, weather, and sports that happen in regions like Europe, + Asia, or the United States. A given consumer may be interested in + all weather messages, regardless of region, or it may be + interested in news and weather for the United States, but + uninterested in items for other regions. In this example, each + consumer sets up its own private queues, which receive precisely + the messages that particular consumer is interested in. + </para> + <section role="h3" id="NETUserGuide-RunningthePublishSubscribeExamples"> + <title> + Running + the Publish-Subscribe Examples + </title> + <para> + 1) Make sure your PATH contains the directory + <home>/qpid/lib + </para> + <para> + 2) Make sure that a qpid broker is running: + </para> + + + <programlisting> +$ ps -eaf | grep qpidd + </programlisting> + + + <para> + If a broker is running, you should see the qpidd process in the + output of the above + command. + </para> + <para> + 3) In separate windows, start one or more topic subscribers as + follows: + </para> + + + <programlisting> +$ cd <home>/qpid/examples/direct + </programlisting> + + + <para> + With cygwin: + </para> + + + <programlisting> +$ ./example-pub-sub--Listener.exe [hostname] [portnumber] + </programlisting> + + + <para> + or with mono: + </para> + + + <programlisting> +$ mono ./example-pub-sub-Listener.exe [hostname] [portnumber] + </programlisting> + + + <para> + You will see output similar to this: + </para> + + + <programlisting> +Listening for messages ... +Declaring queue: usa +Declaring queue: europe +Declaring queue: news +Declaring queue: weather + </programlisting> + + + <para> + Each topic consumer creates a set of private queues, and binds + each queue to the amq.topic exchange together with a binding that + indicates which messages should be routed to the queue. + </para> + <para> + 4) In another window, start the topic publisher, which publishes + messages to the amq.topic exchange, as follows: + </para> + + + <programlisting> +$ cd <home>/qpid/examples/direct + </programlisting> + + + <para> + With cygwin: + </para> + + + <programlisting> +$ ./example-pub-sub-Producer.exe [hostname] [portnumber] + </programlisting> + + + <para> + or with mono: + </para> + + + <programlisting> +$ mono ./example-pub-sub-Producer.exe [hostname] [portnumber] + </programlisting> + + + <para> + This program has no output; the messages are routed to the + message queues for each topic_consumer as specified by the + bindings the consumer created. + </para> + <para> + 5) Go back to the window for each topic consumer. You should see + output like this: + </para> + + + <programlisting> +Message: Message 0 from usa +Message: Message 0 from news +Message: Message 0 from weather +Message: Message 1 from usa +Message: Message 1 from news +Message: Message 2 from usa +Message: Message 2 from news +Message: Message 3 from usa +Message: Message 3 from news +Message: Message 4 from usa +Message: Message 4 from news +Message: Message 5 from usa +Message: Message 5 from news +Message: Message 6 from usa +Message: Message 6 from news +Message: Message 7 from usa +Message: Message 7 from news +Message: Message 8 from usa +Message: Message 8 from news +Message: Message 9 from usa +.... +Message: That's all, folks! from weather +Shutting down listener for control +Message: That's all, folks! from europe +Shutting down listener for control + </programlisting> + + + <para> + Now we will examine the code for each of these programs. In each + section, we will discuss only + the code that must be added to the skeleton shown in Section + "Creating and Closing Sessions". + </para> + <!--h3--> + </section> + + + <section role="h3" id="NETUserGuide-PublishingMessagestoaTopicExchange"> + <title> + Publishing + Messages to a Topic Exchange + </title> + + <para> + The first program in the publish/subscribe example, Publisher.cs, + defines two new functions: one that publishes messages to the + topic exchange, and one that indicates that no more messages are + coming. + </para> + <para> + The publishMessages function publishes a series of five messages + using the specified routing key. + </para> + + + <programlisting> +private static void publishMessages(ClientSession session, string routing_key) +{ + IMessage message = new Message(); + // Asynchronous transfer sends messages as quickly as + // possible without waiting for confirmation. + for (int i = 0; i < 10; i++) + { + message.clearData(); + message.appendData(Encoding.UTF8.GetBytes("Message " + i)); + session.messageTransfer("amq.topic", routing_key, message); + } +} + </programlisting> + + + <para> + The noMoreMessages function signals the end of messages using the + control routing key, which is reserved for control messages. + </para> + + + <programlisting> +private static void noMoreMessages(ClientSession session) +{ + IMessage message = new Message(); + // And send a syncrhonous final message to indicate termination. + message.clearData(); + message.appendData(Encoding.UTF8.GetBytes("That's all, folks!")); + session.messageTransfer("amq.topic", "control", message); + session.sync(); +} + </programlisting> + + + <para> + In the main body of the program, messages are published using + four different routing keys, and then the end of messages is + indicated by a message sent to a separate routing key. + </para> + + + <programlisting> +publishMessages(session, "usa.news"); +publishMessages(session, "usa.weather"); +publishMessages(session, "europe.news"); +publishMessages(session, "europe.weather"); + +noMoreMessages(session); + </programlisting> + <!--h3--> + </section> + + <section role="h3" id="NETUserGuide-ReadingMessagesfromtheQueue2"> + <title> + Reading + Messages from the Queue + </title> + + <para> + The second program in the publish/subscribe example, Listener.cs, + creates a local private queue, with a unique name, for each of + the four binding keys it specifies: usa.#, europe.#, #.news, and + #.weather, and creates a listener. + </para> + + + <programlisting> +Console.WriteLine("Listening for messages ..."); +// Create a listener +prepareQueue("usa", "usa.#", session); +prepareQueue("europe", "europe.#", session); +prepareQueue("news", "#.news", session); +prepareQueue("weather", "#.weather", session); + </programlisting> + + + <para> + The prepareQueue() method creates a queue using a queue name and + a routing key supplied as arguments it then attaches a listener + with the session for the created queue and subscribe for this + receiving messages from the queue: + </para> + + + <programlisting> +// Create a unique queue name for this consumer by concatenating +// the queue name parameter with the Session ID. +Console.WriteLine("Declaring queue: " + queue); +session.queueDeclare(queue, Option.EXCLUSIVE, Option.AUTO_DELETE); + +// Route messages to the new queue if they match the routing key. +// Also route any messages to with the "control" routing key to +// this queue so we know when it's time to stop. A publisher sends +// a message with the content "That's all, Folks!", using the +// "control" routing key, when it is finished. + +session.exchangeBind(queue, "amq.topic", routing_key); +session.exchangeBind(queue, "amq.topic", "control"); + +// subscribe the listener to the queue +IMessageListener listener = new MessageListener(session); +session.attachMessageListener(listener, queue); +session.messageSubscribe(queue); + </programlisting> + <!--h3--> + </section> + <!--h2--> + </section> + + <section role="h2" id="NETUserGuide-WritingRequest-2FResponseApplications"> + <title> + Writing + Request/Response Applications + </title> + + <para> + In the request/response examples, we write a server that accepts + strings from clients and converts them to upper case, sending the + result back to the requesting client. This example consists of + two programs. + </para> + <itemizedlist> + <listitem> + <para>Client.cs is a client application that sends messages to the + server. + • Server.cs is a service that accepts messages, converts + their content to upper case, and sends the result to the + amq.direct exchange, using the request's reply-to property as + the routing key for the response. + </para> + </listitem> + </itemizedlist> + <section role="h3" id="NETUserGuide-RunningtheRequest-2FResponseExamples"> + <title> + Running + the Request/Response Examples + </title> + <para> + 1) Make sure your PATH contains the directory + <home>/qpid/lib + </para> + <para> + 2) Make sure that a qpid broker is running: + </para> + + + <programlisting> +$ ps -eaf | grep qpidd + </programlisting> + + + <para> + If a broker is running, you should see the qpidd process in the + output of the above + command. + </para> + <para> + 3) Run the server. + </para> + <para> + $ cd <home>/qpid/examples/direct + </para> + + + <programlisting> + With cygwin: + </programlisting> + + + <para> + $ ./example-request-response-Server.exe [hostname] [portnumber] + </para> + + + <programlisting> + or with mono: + </programlisting> + + + <para> + $ mono ./example-request-response-Server.exe [hostname] [portnumber] + </para> + + + <programlisting> + You will see output similar to this: + </programlisting> + + + <para> + Waiting for requests + </para> + + + <programlisting> +4) In a separate window, start a client: + +$ cd <home>/qpid/examples/direct + </programlisting> + + + <para> + With cygwin: + </para> + + + <programlisting> +$ ./example-request-response-Client.exe [hostname] [portnumber] + </programlisting> + + + <para> + or with mono: + </para> + + + <programlisting> +$ mono ./example-request-response-Client.exe [hostname] [portnumber] + </programlisting> + + + <para> + You will see output similar to this: + </para> + + + <programlisting> +Activating response queue listener for: clientSystem.Byte[] +Waiting for all responses to arrive ... +Response: TWAS BRILLIG, AND THE SLITHY TOVES +Response: DID GIRE AND GYMBLE IN THE WABE. +Response: ALL MIMSY WERE THE BOROGROVES, +Response: AND THE MOME RATHS OUTGRABE. +Shutting down listener for clientSystem.Byte[] +Response: THAT'S ALL, FOLKS! + </programlisting> + + + <para> + 4) Go back to the server window, the output should be similar to + this: + </para> + + + <programlisting> +Waiting for requests +Request: Twas brillig, and the slithy toves +Request: Did gire and gymble in the wabe. +Request: All mimsy were the borogroves, +Request: And the mome raths outgrabe. +Request: That's all, folks! + </programlisting> + + + <para> + Now we will examine the code for each of these programs. In each + section, we will discuss only the code that must be added to the + skeleton shown in Section "Creating and Closing Sessions". + </para> + <!--h3--> + </section> + + + <section role="h3" id="NETUserGuide-TheClientApplication"> + <title> + The Client + Application + </title> + + <para> + The first program in the request-response example, Client.cs, + sets up a private response queue to receive responses from the + server, then sends messages the server, listening to the response + queue for the server's responses. + </para> + + + <programlisting> +string response_queue = "client" + session.getName(); +// Use the name of the response queue as the routing key +session.queueDeclare(response_queue); +session.exchangeBind(response_queue, "amq.direct", response_queue); + +// Create a listener for the response queue and listen for response messages. +Console.WriteLine("Activating response queue listener for: " + response_queue); +IMessageListener listener = new ClientMessageListener(session); +session.attachMessageListener(listener, response_queue); +session.messageSubscribe(response_queue); + </programlisting> + + + <para> + Set some properties that will be used for all requests. The + routing key for a request is request. + The reply-to property is set to the routing key for the client's + private queue. + </para> + + + <programlisting> +IMessage request = new Message(); +request.DeliveryProperties.setRoutingKey("request"); +request.MessageProperties.setReplyTo(new ReplyTo("amq.direct", response_queue)); + </programlisting> + + + <para> + Now send some requests... + </para> + + + <programlisting> +string[] strs = { + "Twas brillig, and the slithy toves", + "Did gire and gymble in the wabe.", + "All mimsy were the borogroves,", + "And the mome raths outgrabe.", + "That's all, folks!" + }; +foreach (string s in strs) +{ + request.clearData(); + request.appendData(Encoding.UTF8.GetBytes(s)); + session.messageTransfer("amq.direct", request); +} + </programlisting> + + + <para> + And wait for responses to arrive: + </para> + + + <programlisting> +Console.WriteLine("Waiting for all responses to arrive ..."); +Monitor.Wait(session); + </programlisting> + <!--h3--> + </section> + + <section role="h3" id="NETUserGuide-TheServerApplication"> + <title> + The Server + Application + </title> + + <para> + The second program in the request-response example, Server.cs, + uses the reply-to property as the routing key for responses. + </para> + <para> + The main body of Server.cs creates an exclusive queue for + requests, then waits for messages to arrive. + </para> + + + <programlisting> +const string request_queue = "request"; +// Use the name of the request queue as the routing key +session.queueDeclare(request_queue); +session.exchangeBind(request_queue, "amq.direct", request_queue); + +lock (session) +{ + // Create a listener and subscribe it to the request_queue + IMessageListener listener = new MessageListener(session); + session.attachMessageListener(listener, request_queue); + session.messageSubscribe(request_queue); + // Receive messages until all messages are received + Console.WriteLine("Waiting for requests"); + Monitor.Wait(session); +} + </programlisting> + + + <para> + The listener's messageTransfer() method converts the request's + content to upper case, then sends a response to the broker, using + the request's reply-to property as the routing key for the + response. + </para> + + + <programlisting> +BinaryReader reader = new BinaryReader(request.Body, Encoding.UTF8); +byte[] body = new byte[request.Body.Length - request.Body.Position]; +reader.Read(body, 0, body.Length); +ASCIIEncoding enc = new ASCIIEncoding(); +string message = enc.GetString(body); +Console.WriteLine("Request: " + message); + +// Transform message content to upper case +string responseBody = message.ToUpper(); + +// Send it back to the user +response.clearData(); +response.appendData(Encoding.UTF8.GetBytes(responseBody)); +_session.messageTransfer("amq.direct", routingKey, response); + </programlisting> + + <!--h3--> + </section> + <!--h2--> + </section> + <!--h1--> + </section> + + + </section> |