You are not logged in.

Dear visitor, welcome to QtForum.org. If this is your first visit here, please read the Help. It explains in detail how this page works. To use all features of this page, you should consider registering. Please use the registration form, to register here or read more information about the registration process. If you are already registered, please login here.

tuxipuxi

Unregistered

1

Monday, October 13th 2003, 6:52pm

Qt Network Tutorial

Qt Socket Tutorial

Note: this tutorial is a translation of a german tutorial written by the forums member anda_skoa. You find
the original version here: http://www.mrunix.de/forums/showthread.php?s=&threadid=32189 . There may be errors in
this translation. If you've found one, please tell me.

This Tutorial is an Introduction to the eventloop-using Qt Socket classes.
Eventloop-using means means in this case, that you can work without thread although you have a non-blocking input/output.

When working with the Eventloop, you should make sure that the functions called by it are working non-blocking and returning fast. In QSocket's Case the slots to the connected signals should work as short as possible.

Building the Example:

First download the tarball here:
http://www.mrunix.de/forums/attachment.php?s=&postid=139638
or here:
http://www.tuxipuxi.de/qtsockettutorial.tar.bz2

The example applications can be built with provided qmake project files. After setting up QTDIR and QMAKESPEC( if you haven't done that, have a look at: http://doc.trolltech.com/3.1/qmake-manual-2.html ) a simple
#> make
is all you need to do.

The main Makefile generates the two sub-Makefiles and thus the applications.

Client Code
The GUI was built using the Qt Designer.
The first interestening part of the code is in the ClientMainWindow constructor.

Source code

1
2
3
4
5
  m_socket = new QSocket(this);
  QObject::connect(m_socket, SIGNAL(connected()), this, SLOT(slotConneted()));
  QObject::connect(m_socket, SIGNAL(connectionClosed()), this, SLOT(slotDisconnected()));
  QObject::connect(m_socket, SIGNAL(error(int)), this, SLOT(slotError(int)));
  QObject::connect(m_socket, SIGNAL(readyRead()), this, SLOT(slotRead()));


Here we are creating an object of the QSocket class and connecting its signals to our slots.

For example the QSocket object emits the connected() signal when the connection establishment succeeded and the error( int ) signal with an integer parameter for the error state if the connection try failed.


Connecting to a Server is very simple:

Source code

1
2
3
4
  QString host = m_host->text();
  int port = m_port->value();

  m_socket->connectToHost(host, port);


The QSocket member connectToHost establishes a connection and returns immediately. The first parameter is the hosts IP adress or hostname( transformed into an IP adress by QDns ).

QSocket is a subclass of QIODevice, thus it can be used in a stream:

Source code

1
2
3
4
5
6
7
8
  QString input = m_input->text();
  if (input.isEmpty()) return;

  if (m_socket->state() == QSocket::Connected)
  {
    QTextStream stream(m_socket);
    stream << input << endl;
  }


This code checks whether the client is connected and, in case of a connection, sends a message to the server.
The trailing endline is very important, because the protocol expects that a transfer consists of a finished line, means with a trailing endline.

In general, it's better to transfer little packets and save the rest in a buffer.
You can see the state of the transfer with the bytesWritten signal and, when finished the current packet, send the next one.

Reading from the socket is as simple as writing: you can use it's QIODevice functionality or the functions provided by QSocket:

Source code

1
2
3
  QString text;
  while (m_socket->canReadLine())
    text += m_socket->readLine();


The above code is from the slot connected to the readyRead signal. This signal is emitted every time new data is available.

Normally, we will get the volume of the arrived data with QSocket::bytesAvailable.

Server
The server also has a small designer-built GUI.

The main server work, the establishment of user connections, is done by the QServerSocket subclass ServerSocket.
QServerSocket can't be instantiiated directly, you must subclass it to implement the pure virtual function newConnection.

Source code

1
2
3
4
5
6
7
void ServerSocket::newConnection(int socketfd)
{
  QSocket* socket = new QSocket(parent());
  socket->setSocket(socketfd);

  emit newClient(socket);
}


The integer parameter in newConnection is the filedescriptor for the new Connection.
We're creating a new QSocket object and let it work with the filedescriptor.
We need to make the new connection available to our server, this is done with a selfdefined signal called newClient( QSocket* ), which takes
the new socket as parameter.

The first part in servermainwindow.cpp is the data-holding class Client:

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Client
{
public:
  Client(QSocket* socket, QListViewItem* item)
    : m_socket(socket), m_item(item), m_num(++m_count) {};

  ~Client() {
    delete m_socket;
    delete m_item;
  }

  inline QSocket* socket() { return m_socket; }

  inline QListViewItem* item() { return m_item; }

  inline int number() { return m_num; }

protected:
    static int m_count;
    QSocket* m_socket;
    QListViewItem* m_item;
    int m_num;
};


This class is more or less grouped data associated with a Client: The socket for communication with the client, The list view item in the gui and the clients number.
The number is used as the clients name.

The class is defined in the .cpp file because it's only interestening for the server class.

In the constructor of the ServerMainWindow class we're using a feature of the Qt containers:

Source code

1
  m_clients.setAutoDelete(true);


setAutoDelete( true ) tells the container to delete a item when it's removed.
We'll use that later to delete all clients with a clear() call.

Our Server acts as a simple chatserver: it forwards incoming messages to all clients.
The transfer is done the same way as in the client, it handles the sockets like QIODevices.

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
void ServerMainWindow::sendToClients(const QString& text)
{
  if (text.isNull()) return;

  // iterate over all clients and send them the text
  QPtrDictIterator<Client> iter(m_clients);
  for (; iter.current() != 0; ++iter)
  {
    QSocket* sock = iter.current()->socket();
    QTextStream stream(sock);
    stream << text;
  }
}



For accessing all entrys of the dictionary we're using an interator.
The Entrys are Client objects and have the socket() function which returns the socket for the connection.

To be able to accept incoming connects we need a ServerSocket:

Source code

1
2
3
4
5
6
7
8
9
  m_server = new ServerSocket(m_port->value(), this);
  if (!m_server->ok())
  {
    delete m_server;
    m_server = 0;
    return;
  }

  QObject::connect(m_server, SIGNAL(newClient(QSocket*)), this, SLOT(slotNewClient(QSocket*)));



If the bind() call succeeded, the function QServerSocket::ok() returns "true", otherwise it returns "false".
if it returns "false", make sure the portnumber is above 1024( or start the app as root ) or choose another one.

We connect the newClient signal with our slot:

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  QListViewItem* item = new QListViewItem(m_list, socket->peerAddress().toString(),
                                                  QString::number(socket->peerPort()));
  Client* client = new Client(socket, item);

  // notify all others about the newcomer
  sendToClients(QString("Server: Client %0 connected\n").arg(client->number()));

  m_clients.insert(socket, client);

  QObject::connect(socket, SIGNAL(connectionClosed()), this, SLOT(slotClientDisconnected()));
  QObject::connect(socket, SIGNAL(readyRead()), this, SLOT(slotSocketRead()));

  // great the newcomer
  QTextStream stream(socket);
  stream << "Server: Hi\nYou are client " << client->number() << endl;


First we are creating a ListViewItem in the GUI, then we create a Client object for internal use and add it to our Dictionary after messaging the other Clients.

We are connecting the signals relevant to datatransfer with out slots as we've done it in our client.

If a Client closes the connection, the connectionClosed signal is emitted and our slot gets called in which we are doing some cleanups.

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  QObject* sender = const_cast<QObject*>(QObject::sender());
  QSocket* socket = static_cast<QSocket*>(sender);

  qDebug("client disconnected");

  //disconnect signals
  socket->disconnect();

  // remove from dict
  Client* client = m_clients.take(socket);

  sendToClients(QString("Server: Client %0 disconnected\n").arg(client->number()));

  delete client;


We need to find from which Client the signal was emitted, this is done by QObject::sender();


Incoming data is similiar to the Clients datahandling:

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  QObject* sender = const_cast<QObject*>(QObject::sender());
  QSocket* socket = static_cast<QSocket*>(sender);

  Client* client = m_clients.find(socket);

  while (socket->canReadLine())
  {
    QString line = socket->readLine(); // read the line of text

    // and send it to everone including the sender
    sendToClients(QString("Client %0: %1").arg(client->number()).arg(line));

    client->item()->setText(2, line.left(line.length()-1));
    m_list->update();
  }


We get the pointer to the Socket which sent the data with QObject::sender(). The pointer is used to find the corresponding entry in our Dictionary. Then we send the data to all clients, including the sender. At the end the Servers GUI has to be updated.

When the server gets closed, all the client have to be removed:

Source code

1
2
3
4
5
6
7
8
  QPtrDictIterator<Client> iter(m_clients);
  for (; iter.current() != 0; ++iter)
  {
    // disconnect the socket's signals from any slots or signals
    iter.current()->socket()->disconnect();
  }

  m_clients.clear(); // delete all clients (m_clients has autodelete ON)


We're iterating trough the Dictionary and disconnecting the signals and slots, then we delete all client objects. This is done by calling clear() on the Dictionary. The Dictionary has AutoDelete enabled, thus all entrys are deleted automatically. At the End, the client Destructor deletes the Socket of the connection, the connection gets closed.

2

Wednesday, August 29th 2007, 5:58am

RE: Qt Network Tutorial

The files are down. Could you please upload a copy of the new files then post the link?

3

Wednesday, May 21st 2008, 12:36am

It's very nice tutorial, but does anyone have similar one for qt4?