GizmoDistribution

tcp.cpp

TCP transport example application.

This example application illustrates how to use the TCP transport class, and the socket server class. The transport classes can be used independantly from the common event and object distribution as shown here, but normally you do not need to bother. The application can be configured to be either a server or a client. If no configuration is given on the command line, the application will be a server listening for connections on port 2774. The server repeatedly send the current time to all connected clients.

The source code can also be found in the examples directory of the GizmoDistribution installation.

.

00001 //***************************************************************************
00002 // File         : tcp.cpp
00003 // Module       : tcp
00004 // Description  : tcp (example application)
00005 // Author       : Anders Sandblad
00006 // Product      : GizmoDistribution
00007 //
00008 // Copyright © 2004- Saab Training Systems AB, Sweden
00009 //
00010 // NOTE:    GizmoDistribution is a toolkit that is used to implement
00011 //          network distributed objects and events in C++
00012 //
00013 //
00014 // Revision History...
00015 //
00016 // Who  Date    Description
00017 //
00018 // XAA  040823  Created file
00019 //
00020 //***************************************************************************
00021 
00022 //---------------------------------------------------------------------------
00023 //
00024 // This example application illustrates how to use the TCP transport class,
00025 // and the socket server class.
00026 //
00027 // The transport classes can be used independantly from the common
00028 // event and object distribution as shown here, but normally you do not
00029 // need to bother.
00030 //
00031 // The application can be configured to be either a server or a client.
00032 // If no configuration is given on the command line, the application
00033 // will be a server listening for connections on port 2774.
00034 // 
00035 // The server repeatedly send the current time to all connected clients.
00036 //
00037 // Command line: tcp [-server <port> | -client <address> <port>]
00038 //
00039 //---------------------------------------------------------------------------
00040 
00041 
00042 // Includes
00043 #include "gzDistributionLibrary.h"
00044 
00045 
00046 // Common constants
00047 static const gzULong MAX_MESSAGE_LENGTH = 256;
00048 static const gzUShort DEFAULT_PORT = 2774;
00049 static const gzHostAddress DEFAULT_ADDRESS = gzHostAddress(127,0,0,1);
00050 static const gzULong SEND_INTERVAL = 5;
00051 
00052 
00053 //-------------------------------- TcpClient --------------------------------
00054 
00055 // TCP client class
00056 class TcpClient : public gzThread
00057 {
00058     public:
00059 
00060         TcpClient();
00061         virtual ~TcpClient();
00062 
00063         gzBool run(const gzHostAddress& address, gzUShort port);
00064         gzVoid stop(gzBool wait = TRUE);
00065 
00066         gzBool isConnected()  const;
00067         
00068 
00069     private:
00070 
00071         gzVoid process();
00072         gzVoid processClient();
00073 
00074         gzUShort m_port;
00075         gzHostAddress m_address;
00076         gzBool m_connected;
00077 
00078         gzDistTransportTCP m_transport;
00079 
00080 };
00081 
00082 
00083 // Constructor
00084 TcpClient::TcpClient()
00085  :  m_port(DEFAULT_PORT),
00086     m_address(DEFAULT_ADDRESS),
00087     m_connected(FALSE)
00088 {
00089 }
00090 
00091 
00092 // Destructor
00093 TcpClient::~TcpClient()
00094 {
00095     stop(TRUE);
00096 }
00097 
00098 
00099 // Run client
00100 gzBool TcpClient::run(const gzHostAddress& address, gzUShort port)
00101 {
00102     if (isRunning())
00103     {
00104         return FALSE;
00105     }
00106 
00107     if (!m_transport.createClient(gzSocketAddress(address, port)))
00108     {
00109         gzDistReportLastError(TRUE);
00110         return FALSE;
00111     }
00112 
00113     m_address = address;
00114     m_port = port;
00115     m_connected = FALSE;
00116 
00117     gzThread::run();
00118 
00119     return TRUE;
00120 }
00121 
00122 
00123 // Stop client
00124 gzVoid TcpClient::stop(gzBool wait)
00125 {
00126     gzThread::stop(wait);
00127 }
00128 
00129 
00130 // Connected?
00131 gzBool TcpClient::isConnected() const
00132 {
00133     return m_connected;
00134 }
00135 
00136 
00137 // Thread entry point
00138 gzVoid TcpClient::process()
00139 {
00140     GZTRACE("Entering TCP client thread");
00141 
00142     GZ_EXCEPTION(try)
00143     {
00144         processClient();
00145     }
00146     GZ_EXCEPTION(catch(gzBaseError& err)
00147     {
00148         GZMESSAGE(GZ_MESSAGE_FATAL, "Exception in TCP client thread: %s", (const char*)err.getErrorString());
00149         gzSleep(100);
00150     })
00151     GZ_EXCEPTION(catch(...)
00152     {
00153         GZMESSAGE(GZ_MESSAGE_FATAL, "Exception in TCP client thread");
00154         gzSleep(100);
00155     })
00156 
00157     GZTRACE("Leaving TCP client thread");
00158 }
00159 
00160 
00161 // Thread implementation
00162 gzVoid TcpClient::processClient()
00163 {
00164     // setup receive buffer 
00165     gzULong length(0);
00166     gzArray<gzUByte> buffer(MAX_MESSAGE_LENGTH);
00167     memset(buffer.getAddress(), 0, buffer.getSize());
00168 
00169     // send server time flag
00170     gzBool sendTime(TRUE);
00171 
00172     // loop
00173     while (!isStopping())
00174     {
00175         if (!m_connected)
00176         {
00177             // try to connect client-side
00178             if (!m_transport.open())
00179             {
00180                 // connection failed
00181                 gzDistError error = gzDistGetLastError(TRUE);
00182                 if (error.getErrorCode() != GZ_DIST_SOCKET_CONNECTION_REFUSED)
00183                 {
00184                     error.reportError();
00185                     gzThread::stop(FALSE); // quit
00186                 }
00187             }
00188             else
00189             {
00190                 // connection succeeded
00191                 m_connected = TRUE;
00192                 GZMESSAGE(GZ_MESSAGE_NOTICE, "Connected to %s:%u", (const char*)m_address.asString(), m_port);
00193             }
00194         }
00195 
00196         // check connection
00197         if (m_transport.isConnected())
00198         {
00199             // connected
00200 
00201             // receive (non-blocking)
00202             gzInt result = m_transport.receive(buffer.getAddress(), buffer.getSize());
00203 
00204             if (result > 0)
00205             {
00206                     // time was received
00207                 GZMESSAGE(GZ_MESSAGE_NOTICE, "Server time is %s (utc)", (const char*)buffer.getAddress());
00208             }
00209             else if (result < 0)
00210             {
00211                 // there was an error
00212                 gzDistReportLastError(TRUE);
00213             }
00214 
00215         }
00216         else
00217         {
00218             // not connected
00219 
00220             if (m_connected)
00221             {
00222                 // connection lost
00223                 m_connected = FALSE;
00224                 GZMESSAGE(GZ_MESSAGE_NOTICE, "Connection to %s:%u was lost", (const char*)m_address.asString(), m_port);
00225             }
00226         
00227         }
00228 
00229         gzSleep(100);
00230 
00231     }
00232 
00233     if (m_transport.isOpen())
00234     {
00235         m_transport.close();
00236     }
00237 
00238 }
00239 
00240 
00241 //-------------------------------- TimeServer --------------------------------
00242 
00243 class TimeServer : public gzThread
00244 {
00245     public:
00246 
00247         TimeServer();
00248 
00249         virtual ~TimeServer();
00250 
00251         gzVoid addConnection(gzDistTransportTCP* connection);
00252 
00253 
00254     private:
00255 
00256         gzVoid process();
00257 
00258         gzVoid processConnections();
00259 
00260 
00261         gzList<gzDistTransportTCP> m_connectionList;
00262 
00263         gzMutex m_connectionLocker;
00264 
00265 };
00266 
00267 
00268 TimeServer::TimeServer()
00269 {
00270 }
00271 
00272 
00273 TimeServer::~TimeServer()
00274 {
00275     stop(TRUE);
00276 
00277     m_connectionLocker.waitLock();
00278     m_connectionList.clearAndDestroy();
00279     m_connectionLocker.unLock();
00280 }
00281 
00282 
00283 gzVoid TimeServer::addConnection(gzDistTransportTCP* connection)
00284 {
00285     m_connectionLocker.waitLock();
00286     m_connectionList.insert(connection);
00287     GZTRACE("%u connections", m_connectionList.entries());
00288     m_connectionLocker.unLock();
00289 }
00290 
00291         
00292 gzVoid TimeServer::process()
00293 {
00294     GZTRACE("Entering time server thread");
00295 
00296     GZ_EXCEPTION(try)
00297     {
00298         processConnections();
00299     }
00300     GZ_EXCEPTION(catch(gzBaseError& err)
00301     {
00302         GZMESSAGE(GZ_MESSAGE_FATAL, "Exception in time server thread: %s", (const char*)err.getErrorString());
00303         gzSleep(100);
00304     })
00305     GZ_EXCEPTION(catch(...)
00306     {
00307         GZMESSAGE(GZ_MESSAGE_FATAL, "Exception in time server thread");
00308         gzSleep(100);
00309     })
00310 
00311     GZTRACE("Leaving time server thread");
00312 }
00313 
00314 
00315 gzVoid TimeServer::processConnections()
00316 {
00317     gzBool send(FALSE);
00318     gzULong lastSend(0);
00319     gzULong systemSeconds = (gzULong)gzTime::systemSeconds();
00320     gzString now = gzTime::now().asString();
00321 
00322     while (!isStopping())
00323     {
00324         m_connectionLocker.waitLock();
00325 
00326         systemSeconds = (gzULong)gzTime::systemSeconds();
00327 
00328         if (systemSeconds % SEND_INTERVAL == 0)
00329         {
00330             if (systemSeconds > lastSend)
00331             {
00332                 send = TRUE;
00333                 lastSend = systemSeconds;
00334                 now = gzTime::now().asString();
00335             }
00336             else
00337             {
00338                 send = FALSE;
00339             }
00340         }
00341         else
00342         {
00343             send = FALSE;
00344         }
00345 
00346         gzListIterator<gzDistTransportTCP> iter(m_connectionList);
00347         gzDistTransportTCP* connection = NULL;
00348 
00349         while (connection = iter())
00350         {
00351             if (!connection->isConnected())
00352             {
00353                 gzSocketAddress address = connection->getClientConnectAddress();
00354                 GZMESSAGE(GZ_MESSAGE_NOTICE, "Connection to %s:%u was lost", (const char*)address.getHostAddress().asString(), address.getPort());
00355                 
00356                 iter.remove();
00357                 delete connection;
00358 
00359                 GZTRACE("%u connections", m_connectionList.entries());
00360 
00361             }
00362             else if (send)
00363             {
00364                 if (!connection->send((gzUByte*)now.getString(), now.length()+1))
00365                 {
00366                     // there was an error
00367                     gzDistReportLastError(TRUE);
00368                 }
00369             }
00370         }
00371 
00372         m_connectionLocker.unLock();
00373 
00374         gzSleep(100);
00375     }
00376 }
00377 
00378 
00379 //-------------------------------- TcpServer --------------------------------
00380 
00381 class TcpServer : public gzDistSocketServer
00382 {
00383     public:
00384 
00385         TcpServer();
00386 
00387         virtual ~TcpServer();
00388 
00389 
00390     private:
00391 
00392         gzVoid onConnect(gzSocket* socket);
00393 
00394         TimeServer m_timeServer;
00395 
00396 };
00397 
00398 
00399 // Constructor
00400 TcpServer::TcpServer()
00401 {
00402     // run time server
00403     m_timeServer.run();
00404 }
00405 
00406 
00407 // Destructor
00408 TcpServer::~TcpServer()
00409 {
00410     // stop server
00411     stop(TRUE);
00412     
00413     // stop time server
00414     m_timeServer.stop(TRUE);
00415 }
00416 
00417 
00418 // Handle new connections
00419 gzVoid TcpServer::onConnect(gzSocket* socket)
00420 {
00421     // create transport
00422     gzDistTransportTCP* transport = new gzDistTransportTCP; 
00423     
00424     if (!transport->createClient(socket))
00425     {
00426         GZMESSAGE(GZ_MESSAGE_WARNING, "Failed to create transport");
00427         delete transport;
00428         delete socket;
00429     }
00430     else
00431     {
00432         gzSocketAddress address = socket->getConnectAddress();
00433         GZMESSAGE(GZ_MESSAGE_NOTICE, "Connection from %s:%u", (const char*)address.getHostAddress().asString(), address.getPort());
00434 
00435         // add connection to time server
00436         m_timeServer.addConnection(transport);
00437     }
00438 
00439 }
00440 
00441 
00442 //------------------------- Application entry point -------------------------
00443 
00444 int main(int argc, char* argv[])
00445 {
00446     // set message level
00447 #ifdef GZ_DEBUG
00448     gzMessage::setMessageLevel(GZ_MESSAGE_DEBUG);
00449 #else
00450     gzMessage::setMessageLevel(GZ_MESSAGE_NOTICE);
00451 #endif
00452 
00453     // default parameters
00454     gzBool server(TRUE);
00455     gzUShort port(DEFAULT_PORT);
00456     gzHostAddress address(DEFAULT_ADDRESS);
00457 
00458     
00459     // parse command line
00460     gzArgumentParser args(argc, argv);
00461 
00462     if (args.hasOption("server"))
00463     {
00464         server = TRUE;
00465         port = args.getOptionValue("server", DEFAULT_PORT, 0);
00466     }
00467     else if (args.hasOption("client"))
00468     {
00469         server = FALSE;
00470         gzString addressString = args.getOptionValue("client", DEFAULT_ADDRESS.asString(), 0);
00471         address = gzHostAddress(addressString);
00472         port = args.getOptionValue("client", DEFAULT_PORT, 1);
00473     }
00474 
00475 
00476     // run
00477     GZMESSAGE(GZ_MESSAGE_NOTICE, "Hit return to quit!");
00478     
00479     if (server)
00480     {       
00481         GZMESSAGE(GZ_MESSAGE_NOTICE, "Server listening on port %u", port);
00482 
00483         TcpServer server;
00484         server.run(port);
00485 
00486         getchar(); // wait
00487         
00488         server.stop();
00489     }
00490     else
00491     {
00492         GZMESSAGE(GZ_MESSAGE_NOTICE, "Client connecting to port %u on host %s", port, (const char*)address.asString());
00493 
00494         TcpClient client;
00495         client.run(address, port);
00496         
00497         getchar(); // wait
00498 
00499         client.stop();
00500     }
00501 
00502     return 0;
00503 
00504 }
00505 

Documentation for GizmoDistribution generated at Wed Feb 20 11:59:21 2008 by   Saab Training Systems AB, ¸ (c) 2003-and beyond