
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