19.3 ESTABLISHING SOCKET CONNECTIONS WITH EXISTING SERVERS IN C


19.3 ESTABLISHING SOCKET CONNECTIONS WITH EXISTING SERVERS IN C++

We will now show how C++ classes can be used for network programming in a manner very similar to Java. The goal of this section is to show a C++ program that works in the same way as the ClientSocket. java program of Section 19.1. This we will do with the help of the Qt classes for networkprogramming

The Qt library offers several C++ classes with the same high-level functionality as the Java classes we have used so far for network programming. For example, the class QSocket, like the Java class java.net.Socket, can be used to create a buffered TCP socket connection with a named server on a designated port. And the Qt class QServerSocket can be used to create TCP server sockets, like the java.net.ServerSocket class. Other Qt classes useful for network programming are QDns for asynchronous domain name server (DNS) lookup; QNetworkProtocol to serve as a base class for implementing new network protocols; QFtp, derived from QNetworkProtocol, that implements the FTP protocol; QUrlOperator for operating on hierarchical structures (like file systems) using URL's; and so on.

As mentioned already, the purpose of this section is to show a Qt program that does exactly the same thing as the Java program ClientSocket.java of Section 19.1. A client should be able to use the Qt program to set up a socket connection with a designated HTTPD server, to transmit to the server an appropriately formatted request for a web page whose address is embedded in the request string, and to then download the raw ascii of this web page.

The program shown below constructs a new TCP socket object in an idle state (not yet connected to the server) by invoking the QSocket constructor:[4]

     QSocket* socket = new QSocket(); 

The socket thus constructed is connected to a named host by invoking

     socket->connectToHost( qstr, 80 );   // asynchronous call 

where the first argument, of type QString&, is the name of the server, and the second argument, of type Q_UINT16, is the port number on which a socket link is sought. QString is Qt's version of the string class from the C++ Standard Library and the type Q_UINT16 is Qt's 16-bit unsigned short integer.

The function connectToHost, like many other network related functions in the Qt library, works asynchronously, meaning that it returns before the designated operation has been completed. Whether or not a connection was really made and the state of that connection can be checked by trapping the signals emitted by a QSocket object.[5] It is the programmer's job to provide slots for processing these signals and for connecting the signals with their respective slots. After connectToHost is invoked, the signals emitted by a QSocket object are

     void hostFound()                  // host lookup succeeded       void connected()                  // connection successfully                                       // established     void connectionClosed()           // when the host closes the                                       // connection     void delayedClosedFinished()      // if you invoke close() to close                                       // a connection and there is                                       // buffered output data to be                                       // written, the socket object                                       // goes into the QSocket::Closing                                       // state and returns immediately                                       // from the call to close();                                       // the data will continue to be                                       // output until done at that time                                       // this signal is emitted.     void readyRead()                  // means there is incoming data                                       // to be read; this signal is                                       // issued only once each time                                       // there is fresh incoming data                                       // to be read.          void bytesWritten( int nbytes )   // this signal is emitted when                                       // data is actually written to                                        // the network; the nbytes                                       // parameters says how many                                       // bytes were written.     void error( int )                 // an error occurred 

With regard to the signal delayedClosedFinished, if a client invokes close on a TCP connection and there is still buffered output data to be written out, the QSocket object goes into the state QSocket::Closing and the output of the data continues. Since, like the connectToHost function, close is an asynchronous function, it returns immediately. So when a client program requests close, it is a good practice to check if the state of the connection has switched into QSocket::Closing. If that's indeed the case, you'd want to trap the signal delayedClosedFinished to determine the actual close of the connection.

The state of a QSocket object can be queried by invoking the function state on the object. A connection can be in one of the following states defined through an enumeration for QSocket:

    Idle           // if there is no connection    HostLookup     // during DNS lookup    Connecting     // while a TCP connection is being established    Connection     // while there is an operational connection       Closing        // if client has invoked close() on a                   // connection, but there is still data in                   // the output buffer 

The int argument of the error signal, error(int), that is emitted by a QSocket object can be processed by a programmer-supplied slot function to figure out the nature of the error. The different errors correspond to the following symbolic names defined through an enumeration:

     ErrConnectionRefused        // if the connection was refused     ErrHostNotFound             // if the host was not found     ErrSocketRead               // if a read from the socket failed 

The following program uses the QSocket class to establish a TCP connection with an HTTPD server. The program consists of the following three files:

     ClientSocket.h     ClientSocket.cc     Makefile 

The header file ClientSocket.h has the class declaration as shown below:

 
//ClientSocket.h #ifndef CLIENTSOCKET_H #define CLIENTSOCKET_H #include <qsocket.h> #include <string> class ClientSocket : public QSocket { Q_OBJECT string wwwName; QSocket* socket; public: ClientSocket( string wwwName ); string constructHttpRequest(); void socketClosed(); ~ClientSocket(); public slots: void reportConnected(); //(A) void reportHostFound(); //(B) void getWebPage(); //(C) void socketConnectionClosed(); //(D) void reportError(int); //(E) }; #endif

The implementation code is in the file ClientSocket.cc shown below. This code consists mainly of connecting the signals with the slots declared in lines (A) through (E) above and placing appropriate code in the slots. The signal hostFound is connected with the slot reportHostFound in line (F). All that this slot function, defined in line (M), does is to print out on the terminal a message that the hostname lookup was successful. More useful work is undertaken by the slot function reportConnected, defined in line (N), which first reports to us that a connection was successfully established with the server and then proceeds to send to the server a special string that elicits a web page from the server. The string transmitted to the server is the same as in the Java program ClientSocket. java of Section 19.1. When the web page sent by the server in response to the request from the client becomes available to the client, the QSocket object on the client side emits the signal readyRead. The getWebPage slot of line (O), connected to the signal readyRead in line (H), actually reads the incoming bytes one line at a time and displays them on the terminal screen through the following while loop:

      while ( socket->canReadLine() )          cout << socket->readLine(); 

When the server closes the connection, the QSocket object emits the connectionClosed signal. Through the connection established in line (I), this signal fires up the slot function socketConnectionClosed defined in line (P), which eventually invokes the socketClosed function to terminate the client program. The testing that socketConnectionClosed carries out to check whether the state of the socket is Closing is unnecessary in this case because the server makes the web page available only after receiving the entire specially formatted HTTP request string. Since the client program does not transmit anything to the server after the request string, there will be nothing in the output buffer to cause the state of the socket to transition to Closing.

 
//ClientSocket.cc #include "ClientSocket.h" #include <qapplication.h> #include <qsocket.h> #include <string> #include <iostream> using namespace std; ClientSocket::ClientSocket( string siteName ) : QSocket( 0 , 0) { wwwName = siteName; socket = new QSocket( ); connect( socket, SIGNAL( connected() ), this, SLOT( reportConnected() ) ); //(F) connect( socket, SIGNAL( hostFound() ) ), this, SLOT( reportHostFound() ) ); //(G) connect( socket, SIGNAL( readyRead() ), this, SLOT( getWebPage() ) ); //(H) connect( socket, SIGNAL( connectionClosed( ) ), this, SLOT( socketConnectionClosed() ) ); //(I) connect( socket, SIGNAL(error(int) ), this, SLOT( reportError( int ) ) ); //(J) QString qstr( wwwName.c_str() ); socket->connectToHost( qstr, 80 ); // asynchronous call //(K) } ClientSocket::~ClientSocket() {} string ClientSocket::constructHttpRequest() { //(L) char urlArr[256]; string prefix = "http://"; int i = 0; while ( i < prefix.length() ) { urlArr[ i ] = prefix[ i ]; i++; } while ( i < ( wwwName.length() + prefix.length() ) ) { urlArr[i] = wwwName[ i - prefix.length() ] i++; } while ( i < 256 ) { urlArr[ i ] = ' '; i++; } urlArr[255] = 0; string urlString( urlArr ); string httpRequestString = "GET " + urlString + " /HTTP/1.1\n\n"; return httpRequestString; } void ClientSocket::reportHostFound() { //(M) cout << "host found" >> endl; } void ClientSocket::reportConnected() { //(N) cout << "connection established" << endl; string httpRequest = constructHttpRequest(); int len = httpRequest.size(); socket->writeBlock( httpRequest.c_str(), len ); } void ClientSocket::getWebPage() { //(O) cout << "socket ready to read" << endl; int howManyBytes = socket->bytesAvailable(); cout << "bytes available: " << howManyBytes << endl; while (socket->canReadLine()) cout << socket->readLine(); } void ClientSocket::socketConnectionClosed() { //(P) socket->close(); if (socket->state() == QSocket::Closing) { // delayed close connect(socket, SIGNAL(delayedCloseFinished()), this, SLOT(socketClosed()); } else { // The socket is really closed socketClosed(); } } void ClientSocket::reportError(int e) { //(Q) cout << "error report from connectToHost" << endl; cout << "error id: " << e; } void ClientSocket::socketClosed() { cout << "Connection closed" << endl; exit(0); } int main(int argc, char* argv[]) { QApplication app(argc, argv); ClientSocket* sock = new ClientSocket(argv[1]); return app.exec(); }

Shown below is a makefile for the program. It has the same structure as the makefiles for Qt programs shown earlier in Chapter 17. As the makefile shows, using the moc compiler we first carry out a meta object compilation of the header file ClientSocket.h to generate moc_ClientSocket.cc, which is then compiled with g++ to generate moc_ClientSocket.o. At the same time, the implementation code ClientSocket.cc is compiled into ClientSocket.o. Finally, we link moc_ClientSocket.o and ClientSocket.o to generate the executable ClientSocket. The makefile is executed by the command line

     make -f Makefile_ClientSocket

 
#Makefile_ClientSocket CC=g++ #for static and dynamic linking LDLIBS=-L$(QTDIR)/lib -lqt #for compilation CFLAGS=-g -I$(QTDIR)/include ClientSocket: moc_ClientSocket.o ClientSocket.o Makefile_ClientSocket $(CC) $(LDLIBS) -o ClientSocket moc_ClientSocket.o \ ClientSocket.omoc_ClientSocket.cc: ClientSocket.h moc -o moc_ClientSocket.cc ClientSocket.h moc_ClientSocket.o: moc_ClientSocket.cc $(CC) -c $(CFLAGS) -O2 moc_ClientSocket.cc ClientSocket.o: ClientSocket.cc ClientSocket.h $(CC) -c $(CFLAGS) -O2 ClientSocket.cc clean: rm -f ClientSocket rm -f *.o rm -f moc*.*

The executable can be invoked in a command line like

     ClientSocket www.purdue.edu 

if you wanted to see in your terminal window the ascii content of the http://www.purdue.edu web page.

[4]The constructor invocation shown is a default-argument version of the QSocket constructor whose prototype is

           QSocket::QSocket(QObject* parent = 0, const char* name = 0) 

where parent and name arguments can be used in the same manner as in Chapter 17 for establishing parent-child containment relationships in a Qt program.

[5]See Chapter 17 for Qt's signal—slot mechanism.




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

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