In this chapter, we will learn more about the structure and features of the AMPS C/C++ library, and build our first C/C++ program using AMPS.
Connecting to AMPS
Let’s begin by writing a simple program that connects to an AMPS server and sends a single message to a topic:
#include <amps/ampsplusplus.hpp>
#include <iostream>
int main(void)
{
const char* uri = "tcp://127.0.0.1:9007/amps/json";
// Construct a client with the name "examplePublisher".
AMPS::Client ampsClient("examplePublisher");
try
{
// connect to the server and log on
ampsClient.connect(uri);
ampsClient.logon();
// publish a JSON message
ampsClient.publish("messages",
R"({ "message" : "Hello, World!" ,)"
R"("client" : 1 })");
}
catch (const AMPS::AMPSException& e)
{
std::cerr << e.what() << std::endl;
exit(1);
}
return 0;
}
In the example above, we show the entire program.
Future examples will isolate one or more specific portions of the code. The next section describes how to build and run the application and explains the code in further detail.
Build and run
To build the program that you've created:
Create a new .cpp file and use your c compiler to build it, making sure the amps-c++-client/include directory is in your compiler’s include path
Link to the libamps.a or amps.lib static libraries.
Additionally, link to any operating system libraries required by AMPS; a full list may be found by examining the Makefile and project files in the samples directory.
If the message is published successfully, there is no output to the console. We will demonstrate how to create a subscriber to receive messages in Subscriptions section.
Examining the code:
Let us revisit the code we listed earlier:
/* These are the include files required for an AMPS C++ Client. The
* first is <ampsplusplus.hpp>. This header includes everything needed to
* compile C++ programs for AMPS. The next include is the Standard C++ Library
* <iostream>, necessary due to use of std::cerr and std::endl.
*/
#include <amps/ampsplusplus.hpp>
#include <iostream>
int main()
{
/* The URI to use to connect to AMPS. The URI consists of the transport,
* the address, and the protocol to use for the AMPS connection. In this case,
* the transport is tcp, the address is 127.0.0.1:9007, and the protocol is
* amps. In this case, AMPS is configured to allow any message type on that
* transport, so we specify json in the URI to let AMPS know which message
* type this connection will use. Even though a transport that uses the
* amps protocol can accept multiple message types, each connection must specify
* the exact message type that connection will use. Check with the person who
* manages the AMPS instance to get the connection string to use for your programs.
*/
const char* uri = "tcp://127.0.0.1:9007/amps/json";
/* This is where we first interact with AMPS by instantiating an AMPS::Client
* object. Client is the class used to connect to and interact with an AMPS
* server. We pass the string "exampleClient" as the clientName. This name
* will be used to uniquely identify this client to the server. Errors relating
* to this connection will be logged with reference to this name, and AMPS uses
* this name to help detect duplicate messages. AMPS enforces uniqueness for
* client names when a transaction log is configured, and it is good practice
* to always use unique client names.
*/
AMPS::Client ampsClient("exampleClient");
/* Here we open a try block. AMPS C++ classes throw exceptions to indicate
* errors. For the remainder of our interactions with AMPS, if an
* error occurs, the exception thrown by AMPS will be caught and handled
* in the exception handler below.
*/
try
{
/* At this point, we establish a valid AMPS network connection and
* can begin to use it to publish and subscribe to messages. In this
* example, we use the URI specified earlier in the file. If any errors
* occur while attempting to connect to AMPS, the connect() method will
* throw an exception.
*/
ampsClient.connect(uri);
/* The AMPS logon() command connects to AMPS and creates a named
* connection. This version of the logon() command uses a DefaultAuthenticator,
* which uses the credentials provided in the URI. Without credentials, the
* client logs on to AMPS anonymously. AMPS versions 5.0 and later
* require a logon() command in the default configuration.
*
* If you need to use a different authentication scheme, implement an
* Authenticator and pass that Authenticator to this command.
*/
ampsClient.logon();
/* publish a JSON message
* Here, a single message is published to AMPS on the messages topic,
* containing the data Hello world. This data is placed into an XML
* message and sent to the server. Upon successful completion of this
* function, the AMPS client has sent the message to the server, and
* subscribers to the messages topic will receive this Hello world message.
*/
ampsClient.publish("messages",
R"({ "message" : "Hello, World!" ,)"
R"( "client" : 1 })");
}
/* Error handling begins with the catch block. All exceptions thrown by
* AMPS derive from AMPSException classes. More specific exceptions may
* be caught to handle certain conditions, but catching
* AMPSException& allows us to handle all AMPS errors in one
* place. In this example, we print out the error to the console and exit
* the program.
*/
catch (const AMPS::AMPSException& e)
{
std::cerr << e.what() << std::endl;
exit(1);
}
/* At this point we return from main() and our ampsClient object falls
* out of scope. When this happens AMPS automatically disconnects from the
* server and frees all of the client resources associated with the
* connection. In the AMPS C++ client, objects are reference-counted,
* meaning that you can safely copy a client, for example, and destroy copies
* of client without worrying about premature closure of the server connection
* or memory leaks.
*/
return 0;
}
About Authentication
When a client logs on to AMPS, the client sends AMPS a username and password.
The username is derived from the URI, using the standard syntax for providing a user name in a URI, for example, tcp://JohnDoe:@server:port/amps/messagetype to include the user name JohnDoe in the request.
For a given user name, the password is provided by an Authenticator. The AMPS client distribution includes a DefaultAuthenticator that simply returns the password, if any, provided in the URI. A logon() command that does not specify an Authenticator will use an instance of DefaultAuthenticator.
If your authentication system requires a different authentication token, you can implement an Authenticator that provides the appropriate token.