Fortune Client Example

This example uses a simple QDataStream-based data transfer protocol to request a line of text from a fortune server (from the Fortune Server example). The client requests a fortune by simply connecting to the server. The server then responds with a 16-bit (quint16) integer containing the length of the fortune text, followed by a QString.

The asynchronous (non-blocking) approach. Operations are scheduled and performed when control returns to Qt's event loop. When the operation is finished, QTcpSocket emits a signal. For example, QTcpSocket::connectToHost() returns immediately, and when the connection has been established, QTcpSocket emits connected().

The synchronous (blocking) approach. In non-GUI and multithreaded applications, you can call the waitFor...() functions (e.g., QTcpSocket::waitForConnected()) to suspend the calling thread until the operation has completed, instead of connecting to signals.

Other than the widgets that make up the GUI, the data members include a QTcpSocket pointer, a copy of the fortune text currently displayed, and the size of the packet we are currently reading (more on this later).

The socket is initialized in the Client constructor. We'll pass the main widget as parent, so that we won't have to worry about deleting the socket:

In this slot, we initialize blockSize to 0, preparing to read a new block of data. Because we allow the user to click Get Fortune before the previous connection finished closing, we start off by aborting the previous connection by calling QTcpSocket::abort(). (On an unconnected socket, this function does nothing.) We then proceed to connecting to the fortune server by calling QTcpSocket::connectToHost(), passing the hostname and port from the user interface as arguments.

The protocol is based on QDataStream, so we start by creating a stream object, passing the socket to QDataStream's constructor. We then explicitly set the protocol version of the stream to QDataStream::Qt_4_0 to ensure that we're using the same version as the fortune server, no matter which version of Qt the client and server use.

Now, TCP is based on sending a stream of data, so we cannot expect to get the entire fortune in one go. Especially on a slow network, the data can be received in several small fragments. QTcpSocket buffers up all incoming data and emits readyRead() for every new block that arrives, and it is our job to ensure that we have received all the data we need before we start parsing. The server's response starts with the size of the packet, so first we need to ensure that we can read the size, then we will wait until QTcpSocket has received the full packet.