4.5 The Client Application

I l @ ve RuBoard

The following client application illustrates how to use the ACE wrapper facades to establish connections, marshal log records, and send the data to our logging server. This example reads lines from standard input, sends each line to the logging server in a separate log record, and stops when it reads EOF from standard input. We first include the ACE header files needed for the classes this code uses. It's a good idea to specifically include all ACE headers corresponding to the classes used. For example, ace/OS .h includes a definition of ACE_DEFAULT_SERVER_HOST, which we use as the default host name .

 #include "ace/OS.h" #include "ace/CDR_Stream.h" #include "ace/INET_Addr.h" #include "ace/SOCK_Connector.h" #include "ace/SOCK_Stream.h" #include "ace/Log_Record.h" #include "ace/streams.h" #include <string> 

We include ace/streams.h , which contains the conditional code to make the use of C++ iostreams more portable. Based on configuration settings in ACE, it includes the proper form of header file (e.g., < iostream > vs. <iostream.h> ). If the iostreams are supplied in the std namespace, the ace/streams.h file brings the classes used into the global namespace.

We start by defining a Logging_Client class whose send() method transmits one ACE_Log_Record over a connected ACE_SOCK_Stream . This class also implements a helper method that returns a reference to its ACE_SOCK_Stream instance and a destructor that closes the underlying socket handle.

 class Logging Client { public:   // Send <log_record> to the server.   int send (const ACE_Log_Record &log_record);   // Accessor method.   ACE_SOCK_Stream &peer () { return logging_peer_; }   // Close the connection to the server.   ~Logging_Client () { logging_peer_.close (); } private:   ACE_SOCK_Stream logging_peer_; // Connected to server. } 

The Logging_Client::send() method implements the sender side of the message framing mechanism described in Sidebar 9 on page 86. This method is portable and interoperable since it uses the insertion operator ( operator<< ) defined on page 79 to marshal the contents of the log record into an ACE_OutputCDR stream.

 1 int Logging_Client::send (const ACE_Log_Record &log_record) {  2   const size_t max_payload_size =  3       4 // type()  4     + 8 // timestamp  5     + 4 // process id  6     + 4 // data length  7     + MAXLOGMSGLEN // data  8     + ACE_CDR::MAX_ALIGNMENT; // padding;  9 10   ACE_OutputCDR payload (max_payload_size); 11   payload << log_record; 12   ACE_CDR::ULong length = payload.total_length (); 13 14   ACE_OutputCDR header (ACE_CDR::MAX_ALIGNMENT + 8); 15   header << ACE_OutputCDR::from_boolean (ACE_CDR_BYTE_ORDER); 16   header << ACE_CDR::ULong (length); 17 18   iovec iov[2]; 19   iov [0].iov_base = header.begin ()->rd_ptr (); 20   iov[0] .iov_len = 8; 21   iov [1].iov_base = payload.begin ()->rd_ptr (); 22   iov[1] .iov_len = length; 23 24   return logging_peer_.sendv_n (iov, 2); 25 } 

Lines 2 “12 We allocate enough space for a complete ACE_Log_Record , insert the contents of log_record into the payload CDR stream, and get the number of bytes used by the stream.

Lines 14 “16 We next create a CDR-encoded header so the receiver can determine the byte order and size of the incoming CDR stream.

Lines 18 “24 We use an iovec to efficiently send the header and payload CDR streams using a single sendv_n () gather-write method call.

Finally, we show the client application's main() function.

 1 int main (int argc, char *argv[])  2 {  3   u_short logger_port =  4     rgc > 1 ? atoi (argv[1]) : 0;  5   const  char  *logger_host  =  6     argc > 2 ? argv[2] : ACE_DEFAULT_SERVER_HOST;  7   int result;  8  9   ACE_INET_Addr server_addr; 10 11   if (logger_port != 0) 12     result = server_addr.set (logger_port, logger_host); 13   else 14     result = server_addr.set ("ace_logger", logger_host); 15   if (result == -1) 16     ACE_ERROR_RETURN((LM_ERROR, 17                      "lookup %s, %p\n", 18                      logger_port == 0 ? "ace_logger" : argv[l], 19                      logger_host), 1); 20 21   ACE_SOCK_Connector connector; 22   Logging_Client logging_client; 23 24   if (connector.connect (logging_client.peer (), 25                          server_addr) < 0) 26     ACE_ERROR_RETURN ((LM_ERROR, 27                       "%p\n", 28                       "connect()"), 29                       1); 30 31   // Limit the number of characters read on each record. 32   cin.width (ACE_Log_Record::MAXLOGMSGLEN); 33   for (;;) { 34     std::string user_input; 35     getline (cin, user_input, '\n'); 36 37     if (!cin   cin.eof ()) break; 38 39     ACE_Time_Value now (ACE_OS::gettimeofday ()); 40     ACE_Log_Record log_record (LM_INFO, now, 41                                ACE_OS::getpid ()); 42     log_record.msg_data (user_input.c_str ()); 43 44     if (logging_client.send (log_record) == -1) 45       ACE_ERROR_RETURN ((LM_ERROR, 46                          "%p\n", "logging_client.send()"), 1); 47   } 48 49   return  0; // Logging_Client destructor closes TCP connection. 50 } 

Lines 3 “19 If the TCP port and host name are passed as command-line arguments, we use them to initialize the network endpoint where the logging server listens passively to accept connections. Otherwise, we use the default settings.

Lines 21 “29 We next try to connect to the logging server using the ACE_INET_Addr initialized above. The connection is attempted via an ACE_SOCK_Connector . If successful, the logging_client object's socket is connected.

Lines 31 “47 Finally, we show the main client event loop, which keeps reading buffers from the standard input and forwarding them to the logging server until an EOF occurs. The ACE_Log_Record class holds the information for logging one record and contains methods to set and get log record data. We use the Logging_Client::send() method defined on page 96 to simplify the ACE_Log_Record marshaling and transmission.

This client logging application is a simplification of the networked logging service in ACE. The actual ACE logging facility is based on the ACE_Log_Msg class. The macros described in Sidebar 10 use the ACE_Log_Msg class (particularly its ACE_Log_Msg::log() method). The logging client code itself is relatively simple after the accidental complexities have been removed. As we continue to develop the logging service in the book, therefore, the examples will focus on servers since they illustrate the most interesting demultiplexing , concurrency, and synchronization challenges.

I l @ ve RuBoard


C++ Network Programming
C++ Network Programming, Volume I: Mastering Complexity with ACE and Patterns
ISBN: 0201604647
EAN: 2147483647
Year: 2001
Pages: 101

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net