You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tqt3/doc/networking.doc

387 lines
18 KiB

/*
This is under development; the text is intended to replace
network.doc.
*/
/*
\page networking.html
\title Network Module
\if defined(commercial)
This module is part of the \link commercialeditions.html TQt Enterprise Edition \endlink.
\endif
\tableofcontents
\section1 Introduction
Qt's networking classes make it straightforward to write
cross-platform networking applications.
The networking classes can be divided into three groups:
\list 1
\i <b>High Level Network Programming</b>
QUrlOperator, QNetworkProtocol (and its QFtp and QHttp subclasses),
along with QNetworkOperator, provide a simple high level API for
performing network operations in a network- and protocol-transparent
manner. For example, using QUrlOperator, you can fetch a file across
the network, with just one line of code.
\omit
When QHttpClient, QFtpClient and QHttpServer are available, we'll
refer to them her.
\endomit
\i <b>Medium Level Network Programming</b>
The QSocket and QServerSocket classes provide an easy-to-use
API for client and server socket programming.
These classes require an event loop and must be used within an
application's GUI thread. They are ideal for end-user
applications that need network access, but they are not suitable for
console applications or for high performance daemons (services).
\i <b>Low Level Network Programming</b>
The QSocketDevice and QDns classes provide low level networking
access. QSocketDevice can be used for UDP as well as TCP/IP, and can
be used in the non-GUI threads of a multi-threaded application. QDns
performs simple hostname lookups, and also more specialised lookups,
such as Mx records, name Ptr records, etc.
These classes are suitable for all types of applications, from GUI
end-user applications to console applications and high performance
daemons (services).
\endlist
The class documentation for each class presents the functionality and
usage of the class. In this document we will explore typical uses of
Qt's networking classes, showing how they are used in practice.
If you are using standard network protocols, such as FTP and HTTP, you
must register them before you can use QUrlOperator. For example, put
the following call in your \c main() function, after creating your
QApplication object:
\code
tqInitNetworkProtocols();
\endcode
\section1 Network- and Protocol-Transparent Operations
Copying files is simple:
\code
QUrlOperator url;
url.copy( QString("ftp://ftp.trolltech.com/qt/source/qt-3.0.0.tar.gz"), "file:/tmp" );
\endcode
The first (source) URL is copied to the second (destination) URL. The
\link QUrlOperator::copy() copy()\endlink function can be used to move
as well as copy.
QUrlOperator also provides \link QUrlOperator::listChildren()
listChildren()\endlink to obtain a list of the files in a given
directory, \link QUrlOperator::mkdir() mkdir()\endlink, to create a
directory, as well as \link QUrlOperator::rename() rename()\endlink
and \link QUrlOperator::remove() remove()\endlink.
\code
QUrlOperator url;
url.get( "http://www.somedomain.com/cgi-bin/lookup.pl?name=Buzz" );
\endcode
The \link QUrlOperator::get() get()\endlink function is used to get
data, in this example from a query issued to a web site. There is also
a \link QUrlOperator::put() put()\endlink function for writing data to
a remote file.
All operations are performed asynchronously. To find out the results
of an operation connect to the QUrlOperator::finished() signal and
examine the QNetworkOperation object passed to it.
The QUrlOperator class also provides other functions and many more
signals to make it easy to track the progress of an operation. These
signals could be used, for example, to provide feedback on progress to
the user.
\section2 Example: Fetching All the Files in a Remote Directory
This example class is used as follows:
\code
FetchFiles ff( "ftp://ftp.trolltech.com" );
connect( &myWidget, stop(), &ff, stop() );
connect( &ff, start(), &myWidget, start() );
connect( &ff, startFile(), &myWidget, startFile() );
connect( &ff, finishedFile(), &myWidget, finishedFile() );
connect( &ff, finished(), &myWidget, finished() );
connect( &ff, error(), &myWidget, error() );
ff.fetch();
\endcode
The FetchFiles constructor takes a URL. When \c fetch() is called, the
FetchFiles object will iterate over every file it finds at the given
URL and download each one that is a regular file, (i.e. ignoring
directories). The FetchFiles object will emit signals as follows:
\list
\i start() -- emitted when it calls listChildren().
\i startFile( QString ) -- emitted once for each file it starts to
copy, parameterised by the filename.
\i finishedFile( QString ) -- emitted once for each file it finishes
copying, parameterised by the filename.
\i finished() -- emitted when all files have been read.
\i error() -- emitted if an error occurs.
\endlist
By connecting the signals to a widget you can keep the user notified
regarding progress.
The object also has a \c stop() slot, which you can connect to. This
is useful if you want the user to be able to cancel the operation
before it is complete.
### Rainer: is this a good example? If it is, I'll try to write the code.
\section3 The FetchFiles header file
\section3 The FetchFiles C++ file
\section1 Client/Server Applications that use Custom Protocols
In this section we'll present creation of the simple Information Server and two Clients - one that uses direct network programming, using QServerSocket and QSocket and the other one that uses QNetworkProtocol subclass.
For that purpose we developed a very simple communication protocol between sever and clients.
Data is stored at server in hierarchical structure (similar to file systems) with folder (directory) and data (file) nodes. Our protocol uses only two directives - \c LIST and \c GET.
Server accepts only those two commands and returns one or more lines of the following format:
"xxxM line_content"
xxx represents the return message code, If character M (More) is '+' that means that it's not the last line of the response, and if M is ' ' (space) than it is the last line.
LIST directory_node_path
Response: "212+T child_address"
It lists all children of given directory node. It returns a line for every child of the directory. T stands for Type and can be 'D' for directories or 'F' for file nodes. Last line of response will always be "212 ".
GET data_node_path
Response: "213+ one_data_line"
GET returns specified file line by line. Last line is always "213 ".
There are two more responses:
"500 File not found" - path points to non-existent node.
"550 Syntax error" - error in command parsing.
\section2 Info Server
First, we'll write a simple server that keeps information data in a tree structure and supports described communication protocol.
Data and supported operations handles \e InfoData class. We will present here just the public interface of that class:
\quotefile network/infoprotocol/infoserver/infodata.h
\skipto class InfoData
\printuntil };
\caption From \l network/infoprotocol/infoserver/infodata.h
\c InfoData::list lists all node children, while \c InfoData::get gets the data file. You can see this class implementation in file \l network/infoprotocol/infoserver/infodata.cpp. For this example, we just hard coded description of one small office network.
The main idea follows: Server creates the SimpleServer object (QServerSocket subclass) which listens on the specified port and, when receives connection request from a client, creates ClientSocket object (QSocket subclass) to handle that connection. ClientSocket recognizes two mentioned operations (list and get), performs them on InfoData object and returns generated response back to the client.
Let us look at Server implementation (\l network/infoprotocol/infoserver/server.cpp):
\quotefile network/infoprotocol/infoserver/server.cpp
\skipto ::SimpleServer
\printuntil exit
\printline }
\printline }
In the SimpleServer base class QServerSocket constructor tries to bind itself to the given port. We use QServerSocket::ok() to find out if that was successful and to detect eventual problems. After that it will monitor that port an call QServerSocket::newConnection() every time a succesful connection with client is made.
\skipto newConnection
\printuntil }
This is reimplemented pure virtual function QServerSocket::newConnection(). It creates ClientSocket which will be responsible for just established incoming connection with \e socket as the file descriptor.
\skipto ClientSocket
\printuntil }
ClientSocket constructor connects QSocket::readyRead() signal which is emitted when something arrives from the Client. We also want to know when connection is terminated. When SimpleServer creates ClientSocket, connection is already established, so we just need to specify used socket with QSocket::setSocket().
\skipto readClient
\printuntil }
\printline }
This slot is called every time we receive some data via socket.
Our communication protocol is textual and line oriented, and socket communication is asynchronous (don't forget that, we don't know when readyRead() will be emitted, or will that be at the end of the line), so we have to check with QSocket::canReadLine() if the full line has been received. Because each input line presents one command in this protocol (list or get) we will process it and return generated answer through the socket back to the Client.
Function processCommand() parses the input line and if it recognizes LIST or GET command, calls corresponding (InfoData*)info methods - list and get, otherwise creates appropriate error message.
QSocket is a subclass of QIODevice, thus we can use QTextStream to read and write lines to it.
\skipto connectionClosed
\printuntil }
When connection closes, ClientSocket is not needed anymore, so it deletes itself. New one will be created upon new connection.
\section2 Info Client
Now, lets see the example of a Client that will use this Server, implemented through direct socket programming with QSocket (\l network/infoprotocol/infoclient/client.cpp)
\quotefile network/infoprotocol/infoclient/client.cpp
\skipto ::connectToServer
\printuntil }
Because user connects to the server by request (btnConnect), connectToServer() dynamically creates new QSocket which will carry out the connection with the server. Signal QSocket::connected() is emitted after connection is successfully performed, and QSocket::error(int) is emitted with error code on any error. Because communication is asynchronous, we can't check if QSocket::connectToHost() succeeded or not. That's why we must rely on signals.
\skipto ::sendToServer
\printuntil os <<
\printline }
This function sends a command to the server. Notice that is uses QTextStream to send data via socket.
\skipto ::socketReadyRead
\printuntil append( line.mid
\printline }
\printline }
\printline }
Here we parse responses from the server. It's worth noting again that the communication is asynchronous and it must not be assumed when and how the data will come. The fact that server socket sends all data lines at once (in one loop) does not mean that client socket will receive them as one package and emit one readyRead() signal. That is why we designed our protocol to have termination line , with M = ' ' (e.g. "213 "). In this example the line code will determine its destination (infoList or infoText), but in more advanced usage client would probably require some sort of the finite state machine, as we'll se in the next example.
\skipto ::socketConnectionClosed()
\printuntil .arg
\printline }
It is not mandatory, of course, but it's good programming practice to cover errors and termination of the connection. Also, the best way to detect if the connecting to host succeeded (using connectToHost()) is use of an error(int) signal.
\section2 Info Url Client
It is time to illustrate how to use QNetworkProtocol, QNetworkOperation and QUrlOperator to register our communication protocol and make it on par to already implemented protocols, like QFtp, QHttp and QLocalFs. This will give us much larger flexibility in use and let us use TQt class that supports Network Protocols, e.g. QFileDialog.
First, here is the header file in which Qip (our custom Network
Protocol) is declared (\c network/infoprotocol/infourlclient/qip.h):
\quotefile network/infoprotocol/infourlclient/qip.h
\skipto Qip
\printuntil };
QNetworkProtocol is the base class for every Network Protocol class. Because this protocol uses network, we embedded one QSocket* member variable to which we'll delegate network communication. Protocols that doesn't require to use network will do it on their own way - e.g. QLocalFs uses QDir, some data acquisition protocol may use serial or USB connection, only requirement is that protocol uses hierarchical structure and can be accessed using URLs (to have addressable nodes).
Let us go to the Qip implementation (\c
network/infoprotocol/infourlclient/qip.cpp):
\quotefile network/infoprotocol/infourlclient/qip.cpp
\skipto Qip
\printuntil }
Because we use QSocket to perform network communication for us we have to initialize \e socket in the similar way we did in previous example.
\skipto supportedOperations
\printuntil }
We have to announce which of the supported operations our protocol supports. For the complete list of available operations, see QNetworkProtocol::Operation.
\skipto checkConnection
\printuntil }
In this function protocol checks if the connection is established, and if not, tries to do so. Again, because of the asynchronous nature of the QSocket, we don't know when and how connectToHost() will be finished, so we need to test if socket are still trying to make a connection.
\skipto operationListChildren
\printuntil GET
\printuntil ;
\printuntil }
Here we implement two supported operations. QUrlOperator is class that initiated our protocol at first (after spotting that url starts with "qip://"), and can be approached with url() function. We'll use it to find path to our node. E.g. if url was "qip://my_server/network/fax/", path() would return "/network/fax/", while host() would be "my_server". For each operation, QNetworkOperation object is created to hold its state and description. We will mark here that current operation started. See all operation states at QNetworkProtocol::State.
\skipto ::socketReadyRead
\printuntil finished
\printuntil }
\printuntil }
\printuntil }
Implementation is very similar to previous example in addition that there are now some signal emitting requirements, so we had to use simple state machine here. In list operation we have to emit start(QNetworkOperation*) before first child, and then to emit QNetworkProtocol::newChild (const QUrlInfo&, QNetworkOperation*) for each child listed from the server. For get operation, we should emit QNetworkProtocol::data (const QByteArray&, QNetworkOperation*) for each data chunk received (in this case, one text line). It is very important that \e every operation finishes with QNetworkProtocol::finished(QNetworkOperation*) signal!
\skipto error
\printuntil state
\printline }
In the case of an error we set error state, code and message, and, important enough and deserves to be mention again, emit finished(QNetworkOperation*) signal.
Now, when we have our QNetworkProtocol Qip implemented, Info Url Client will be much simpler than in previous QSocket example.
But, before we can use our new protocol, we have to register it first, so QUrlOperators can react on it. We have done it in the main.cpp (\l network/infoprotocol/infourlclient/main.cpp):
\quotefile network/infoprotocol/infourlclient/main.cpp
\skipto register
\printline register
This registers Qip protocol and bonds it to prefix "qip". You can use tqInitNetworkProtocols() which registers pre coded Ftp (for "ftp") and Http ("http") protocols. Local file system (QLocalFs) is always registered.
Client implementation (\c network/infoprotocol/infourlclient/client.cpp):
\quotefile network/infoprotocol/infourlclient/client.cpp
\skipto ::ClientInfo
\printuntil }
Qip protocol will send us data lines, our is just to pick them and process. Note that here we don't use finished() signal which we'd like to use if we want to know when the full file is received. In that case, in appropriate slot, we would like to check if it's ( operationInProgress()->operation() == QNetworkProtocol::OpGet ).
\skipto ::downloadFile
\printuntil get
\printuntil }
\printuntil }
Here is where we use some QNetworkProtocol magic. (QUrlOperator)op is constructed from a selected url \e file, and then we just use QUrlOperator::get() to fetch its content.
\skipto ::getOpenFileName
\printuntil return
\printuntil }
This function implements simple QFileDialog that will serve us to browse through the nodes on the server and to select one data node to view. Starting url is "qip://localhost/" which indicates to QFileDialog that we want to use Qip protocol served on the local server. We could also specify the exact port, e.g. "qip://my_server:123" will try to inquire my_server over port 123, otherwise the default port is used.
We didn't use static function QFileDialog::getOpenFileName() because under Windows and Mac OS X, it will usually use the native file dialog and not a QFileDialog, in which case we wouldn't be able to use our protocol at all.
\section2 Creating a Custom Protocol
### Rainer: have we an example that I can use?
\section2 Client Applications that use Custom Protocols
### Rainer: have we an example that I can use?
\section2 Server Applications that use Custom Protocols
### Rainer: have we an example that I can use?
\section1 Client/Server Applications that use Standard Protocols
### Rainer: have we an example that I can use?
\section2 Client Applications that use Standard Protocols
### Rainer: have we an example that I can use?
\section2 Server Applications that use Standard Protocols
### Rainer: have we an example that I can use?
\section1 Inter-Process Communication between TQt Applications
### Rainer: have we an example that I can use?
\section1 Inter-Process Communication between TQt and non-Qt Applications
### Rainer: have we an example that I can use?
*/