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.
387 lines
18 KiB
387 lines
18 KiB
14 years ago
|
/*
|
||
|
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 Qt 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
|
||
|
qInitNetworkProtocols();
|
||
|
\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 Qt 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 qInitNetworkProtocols() 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 Qt Applications
|
||
|
|
||
|
### Rainer: have we an example that I can use?
|
||
|
|
||
|
\section1 Inter-Process Communication between Qt and non-Qt Applications
|
||
|
|
||
|
### Rainer: have we an example that I can use?
|
||
|
|
||
|
|
||
|
*/
|