Sophie

Sophie

distrib > Fedora > 14 > x86_64 > by-pkgid > c200d180bc1063a7706e0da42a546b51 > files > 365

atlascpp-devel-0.6.1-7.fc12.x86_64.rpm

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Atlas-C++: A simple client/server</title>
<link href="tabs.css" rel="stylesheet" type="text/css">
<link href="doxygen.css" rel="stylesheet" type="text/css">
</head><body>
<!-- Generated by Doxygen 1.5.9 -->
<div class="navigation" id="top">
  <div class="tabs">
    <ul>
      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
      <li class="current"><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
      <li><a href="modules.html"><span>Modules</span></a></li>
      <li><a href="namespaces.html"><span>Namespaces</span></a></li>
      <li><a href="annotated.html"><span>Classes</span></a></li>
      <li><a href="files.html"><span>Files</span></a></li>
    </ul>
  </div>
</div>
<div class="contents">
<h1><a class="anchor" name="tutorial_simple">A simple client/server </a></h1>Let's take a look at a really simple Atlas-C++ client and server.<p>
All that the server was is wait for a connection, receive something from the client and wait for the connection to close. The client connects to the server, sends a message and then closes the connection.<p>
Both client and server use libsocket++ (see <a class="el" href="tutorial_preamble.html">Preamble</a>) for socket communications.<h2><a class="anchor" name="general">
General Concepts</a></h2>
<h3><a class="anchor" name="atlas">
About Atlas</a></h3>
I assume you already know a little about the way <a class="el" href="namespace_atlas.html" title="The Atlas namespace.">Atlas</a> works. Broadly speaking it allows the transmission of messages, which can contain objects of different types. These types are integers, floating-point values, character strings, lists of objects and lastly maps (which are keyed lists, such as perl hashes, python dicts, and STL maps).<p>
Atlas-C++ allows you to comfortably send and receive such messages in a stream fashion through iostreams (which can be socket streams), without ever having to deal with the <a class="el" href="namespace_atlas.html" title="The Atlas namespace.">Atlas</a> protocol itself.<h3><a class="anchor" name="codecs">
Codecs</a></h3>
The <a class="el" href="namespace_atlas.html" title="The Atlas namespace.">Atlas</a> "protocol" is an implementation-independent definition. This means you can express atlas messages in different ways, such as in an XML-like fashion, in a simple ASCII protocol or in a very space-efficient binary protocol. Such protocol implementations are called "codecs".<p>
An Atlas-C++ Codec takes an iostream as parameter at construction and has functions for sending the various different types of values (maps, lists and single values). Each codec also provides a function called Poll(). This gets a character from the stream (note that it will block - so check your socket first!) and parses it. "That's great," I hear you say, "but how do I get the data that it receives?" Well, this is where one of the neat things about Atlas-C++ comes in: Bridges.<h3><a class="anchor" name="bridges">
Bridges</a></h3>
Codec is in fact publicly derived from Bridge. Bridge is an abstract class which contains all those functions to send maps, lists and other values. The neat thing about Bridge is though that it is symmetric. That means it's not just used to send messages, but also to receive them.<p>
So, when you create a Codec you pass it a pointer to a Bridge as well. The virtual functions in that Bridge get called whenever parts of a message are received. So, you can put whatever you want to do with that data into a Bridge of your own, pass an instance of that to the Codec, and you will have instant satisfaction! :)<h3><a class="anchor" name="negotiation">
Negotiation</a></h3>
Now, it would be a hassle if you would have to check which codecs the peer you are connecting to has each time you make a new connection, since Atlas-C++ supports any number of different codecs. But fear not, for Atlas-C++ does even this for you. Whenever you connect to a server or client, you first enter a negotiation stage, in which the client and server decide which Codec to use.<p>
This is done by creating a Net::StreamAccept object in the server or a Net::StreamConnect object in the client, passing the name of the server/client, the Bridge to be used by the Codec and the stream on which the connection has been established. You then repeatedly call the Poll() method of these classes until their state is not IN_PROGRESS any more. If negotiation was successful, you can retrieve the codec to be used with GetCodec(), and then go on sending and receiving messages happily with your peer.<p>
If negotation failed, GetState() will return FAILURE, and you may abort the connection and alert the user.<h3><a class="anchor" name="filters">
Filters</a></h3>
Lastly, it should be noted that not only codecs exist, but also filters. These "wrap around" the codecs and do things like bzip2/gzip compression, or encryption. These are represented in Atlas-C++ by the Filter class, and also automatically negotiated by the Negotiation classes. This means that while coding you never actually have to worry about them.<h3><a class="anchor" name="magic">
Automagic detection</a></h3>
You should know that Atlas-C++ will automagically detect what codecs and filters are linked into your application, so you will never need to provide lists of these or any such inconveniences.<h2><a class="anchor" name="server">
The Server</a></h2>
See under the code for more documentation.<p>
<b>Note:</b><p>
If the server does not seem to be accepting connections, try changing bind("0.0.0.0", 6767) to bind("127.0.0.1", 6767) (or other similar addresses that might be appropriate.<p>
<div class="fragment"><pre class="fragment">/* Simple Atlas-C++ Server
 *
 * Part of the Atlas-C++ Tutorial
 *
 * Copyright 2000 Stefanus Du Toit.
 *
 * This file is covered by the GNU Free Documentation License.
 */

// The DebugBridge
#include "DebugBridge.h"
// Atlas negotiation
#include &lt;Atlas/Net/Stream.h&gt;
#include &lt;Atlas/Codec.h&gt;
// tcp_socket_server, tcp_socket_stream - the iostream socket classes
#include &lt;skstream/skserver.h&gt;
// cout, cerr
#include &lt;iostream&gt;

int main(int argc, char** argv)
{
    // This socket accepts connections
    tcp_socket_server listener;
    
    // Bind the socket. 0.0.0.0 accepts on any incoming address
    listener.open(6767);
    std::cout &lt;&lt; "Bound to " &lt;&lt; 6767 &lt;&lt; std::endl;
    std::cout &lt;&lt; "Listening... " &lt;&lt; std::flush;

    // This blocks until a client connects
    tcp_socket_stream client(listener.accept());
    std::cout &lt;&lt; "accepted client connection!" &lt;&lt; std::endl;

    // The DebugBridge puts all that comes through the codec on cout
    DebugBridge bridge;
    // Do server negotiation for Atlas with the new client
    Atlas::Net::StreamAccept accepter("simple_server", client);

    std::cout &lt;&lt; "Negotiating.... " &lt;&lt; std::flush;
    // accepter.Poll() does all the negotiation
    while (accepter.getState() == Atlas::Net::StreamAccept::IN_PROGRESS) {
        accepter.poll();
    }
    std::cout &lt;&lt; "done." &lt;&lt; std::endl;

    // Check the negotiation state to see whether it was successful
    if (accepter.getState() == Atlas::Net::StreamAccept::FAILED) {
        std::cerr &lt;&lt; "Negotiation failed." &lt;&lt; std::endl;
        return 2;
    }
    // Negotiation was successful! 

    // Get the codec that negotation established
    Atlas::Codec * codec = accepter.getCodec(bridge);

    std::cout &lt;&lt; "Polling client..." &lt;&lt; std::endl;
    
    // iosockinet::operator bool() returns false once a connection closes
    while (client) {
        // Get incoming data and process it
        codec-&gt;poll(); // this blocks!
    }

    // The connection closed
    
    std::cout &lt;&lt; "Client exited." &lt;&lt; std::endl;

    return 0;
}
</pre></div><p>
As mentioned before, the server does the following:<p>
<ol>
<li>
Wait for a connection </li>
<li>
Accept a client connection </li>
<li>
Negotiate with the connecting client </li>
<li>
Receive messages from the client and wait for it to disconnect </li>
</ol>
<p>
Only steps 3 and 4 really involve Atlas-C++, the rest is handled by the socket library I'm using (libsocket++, see <a class="el" href="tutorial_preamble.html">Preamble</a>).<h2><a class="anchor" name="client">
The Client</a></h2>
<div class="fragment"><pre class="fragment">/* Simple Atlas-C++ Client
 *
 * Part of the Atlas-C++ Tutorial
 *
 * Copyright 2000 Stefanus Du Toit.
 *
 * This file is covered by the GNU Free Documentation License.
 */

// Atlas negotiation
#include &lt;Atlas/Net/Stream.h&gt;
#include &lt;Atlas/Codec.h&gt;
// The DebugBridge
#include "DebugBridge.h"

// tcp_socket_stream - the iostream-based socket class
#include &lt;skstream/skstream.h&gt;

// cout, cerr
#include &lt;iostream&gt;

#include &lt;map&gt;
#include &lt;list&gt;

// sleep()
#include &lt;unistd.h&gt;

// This sends a very simple message to c
void helloWorld(Atlas::Codec &amp; c)
{
    std::cout &lt;&lt; "Sending hello world message... " &lt;&lt; std::flush;
    c.streamMessage();
    c.mapStringItem("hello", "world");
    c.mapEnd();
    std::cout &lt;&lt; "done." &lt;&lt; std::endl;
}

int main(int argc, char** argv)
{
    // The socket that connects us to the server
    tcp_socket_stream connection;

    std::cout &lt;&lt; "Connecting..." &lt;&lt; std::flush;
    
    // Connect to the server
    if(argc&gt;1) {
      connection.open(argv[1], 6767);
    } else {
      connection.open("127.0.0.1", 6767);
    }
    
    // The DebugBridge puts all that comes through the codec on cout
    DebugBridge bridge;
    // Do client negotiation with the server
    Atlas::Net::StreamConnect conn("simple_client", connection);

    std::cout &lt;&lt; "Negotiating... " &lt;&lt; std::flush;
    // conn.poll() does all the negotiation
    while (conn.getState() == Atlas::Net::StreamConnect::IN_PROGRESS) {
        conn.poll();
    }
    std::cout &lt;&lt; "done" &lt;&lt; std::endl;

    // Check whether negotiation was successful
    if (conn.getState() == Atlas::Net::StreamConnect::FAILED) {
        std::cerr &lt;&lt; "Failed to negotiate" &lt;&lt; std::endl;
        return 2;
    }
    // Negotiation was successful

    // Get the codec that negotiation established
    Atlas::Codec * codec = conn.getCodec(bridge);

    // This should always be sent at the beginning of a session
    codec-&gt;streamBegin();
    
    // Say hello to the server
    helloWorld(*codec);
    connection &lt;&lt; std::flush;

    std::cout &lt;&lt; "Sleeping for 2 seconds... " &lt;&lt; std::flush;
    // Sleep a little
    sleep(2);
    std::cout &lt;&lt; "done." &lt;&lt; std::endl;

    // iosockinet::operator bool() returns false if the connection was broken
    if (!connection) {
        std::cout &lt;&lt; "Server exited." &lt;&lt; std::endl;
    } else {
        // It was not broken by the server, so we'll close ourselves
        std::cout &lt;&lt; "Closing connection... " &lt;&lt; std::flush;
        // This should always be sent at the end of a session
        codec-&gt;streamEnd();
        connection &lt;&lt; std::flush;
        // Close the socket
        connection.close();
        std::cout &lt;&lt; "done." &lt;&lt; std::endl;
    }

    return 0;
}
</pre></div><p>
The client corresponds with the server by doing the following:<p>
<ol>
<li>
Connect to the server </li>
<li>
Negotiate a server connection </li>
<li>
Send a message </li>
<li>
Sleep a little while </li>
<li>
Close the connection </li>
</ol>
<p>
Again, only steps 2, 3 and 5 involve dealing with Atlas-C++.<p>
The code should be pretty straightforward to understand if you have read the <a class="el" href="tutorial_simple.html#general">General Concepts</a>.<h2><a class="anchor" name="debug_bridge">
DebugBridge</a></h2>
In the above client + server a class called DebugBridge is used. This class is not part of Atlas-C++, but rather one that we made ourselves. It is passed to the negotiation classes (StreamConnect and StreamAccept) which then pass it on to the codec returned by GetCodec().<p>
DebugBridge simply prints out everything it receives onto cout. The source code should be quite easily understandable.<p>
<div class="fragment"><pre class="fragment">#ifndef DEBUG_BRIDGE_H
#define DEBUG_BRIDGE_H

#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;Atlas/Bridge.h&gt;

class DebugBridge : public Atlas::Bridge
{
public:

    DebugBridge()
    {
        padding = "";
    }

    virtual ~DebugBridge() { }

    virtual void streamBegin() {
        std::cout &lt;&lt; padding &lt;&lt; "streamBegin" &lt;&lt; std::endl;
        addPadding();
    }
    virtual void streamMessage() {
        std::cout &lt;&lt; padding &lt;&lt; "New Map" &lt;&lt; std::endl;
        addPadding();
    }
    virtual void streamEnd() {
        removePadding();
        std::cout &lt;&lt; padding &lt;&lt; "streamEnd" &lt;&lt; std::endl;
    }
    
    virtual void mapMapItem(const std::string&amp; name)
    {
        std::cout &lt;&lt; padding &lt;&lt; name &lt;&lt; " -&gt; New Map" &lt;&lt; std::endl;
        addPadding();
    }
    virtual void mapListItem(const std::string&amp; name)
    {
        std::cout &lt;&lt; padding &lt;&lt; name &lt;&lt; " -&gt; New List" &lt;&lt; std::endl;
        addPadding();
    }
    virtual void mapIntItem(const std::string&amp; name, long i)
    {
        std::cout &lt;&lt; padding &lt;&lt; name &lt;&lt; " -&gt; Int: " &lt;&lt; i &lt;&lt; std::endl;
    }
    virtual void mapFloatItem(const std::string&amp; name, double d)
    {
        std::cout &lt;&lt; padding &lt;&lt; name &lt;&lt; " -&gt; Float: " &lt;&lt; d &lt;&lt; std::endl;
    }
    virtual void mapStringItem(const std::string&amp; name, const std::string&amp; s)
    {
        std::cout &lt;&lt; padding &lt;&lt; name &lt;&lt; " -&gt; String: " &lt;&lt; s &lt;&lt; std::endl;
    }
    virtual void mapEnd()
    {
        removePadding();
        std::cout &lt;&lt; padding &lt;&lt; "mapEnd" &lt;&lt; std::endl;
    }
    
    virtual void listMapItem()
    {
        std::cout &lt;&lt; padding &lt;&lt; "New Map" &lt;&lt; std::endl;
        addPadding();
    }
    virtual void listListItem()
    {
        std::cout &lt;&lt; padding &lt;&lt; "New List" &lt;&lt; std::endl;
        addPadding();
    }
    virtual void listIntItem(long i)
    {
        std::cout &lt;&lt; padding &lt;&lt; "Int: " &lt;&lt; i &lt;&lt; std::endl;
    }
    virtual void listFloatItem(double d)
    {
        std::cout &lt;&lt; padding &lt;&lt; "Float: " &lt;&lt; d &lt;&lt; std::endl;
    }
    virtual void listStringItem(const std::string&amp; s)
    {
        std::cout &lt;&lt; padding &lt;&lt; "String: " &lt;&lt; s &lt;&lt; std::endl;
    }
    virtual void listEnd()
    {
        removePadding();
        std::cout &lt;&lt; padding &lt;&lt; "listEnd" &lt;&lt; std::endl;
    }

protected:
    virtual void addPadding()
    {
        padding += "  ";
    }

    virtual void removePadding()
    {
        padding.erase(padding.end() - 2, padding.end());
    }

    std::string padding;
};

#endif // DEBUG_BRIDGE_H
</pre></div><p>
<a class="el" href="tutorial_preamble.html">Previous</a> <a class="el" href="tutorial_index.html">Index</a> <a class="el" href="tutorial_message.html">Next</a><p>
<dl class="author" compact><dt><b>Author:</b></dt><dd>Stefanus Du Toit &lt;<a href="mailto:sdt@gmx.net">sdt@gmx.net</a>&gt; </dd></dl>
</div>
<HR>
<P>Copyright 2000-2004 the respective authors.</P>
<P>This document can be licensed under the terms of the GNU Free Documentation
License or the GNU General Public License and may be freely distributed under
the terms given by one of these licenses.</P>