Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Multiple Clients on QTcpServer
Forum Updated to NodeBB v4.3 + New Features

Multiple Clients on QTcpServer

Scheduled Pinned Locked Moved General and Desktop
8 Posts 3 Posters 16.8k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • V Offline
    V Offline
    Vater Frost
    wrote on last edited by
    #1

    Hello,

    I have yet another issue with QTcpServer and sockets. The problem is as follows:

    My server class is inherited from QObject and I have an object of QTcpServer inside the class.
    My clients connect to the server, each socket from client connection is stored in a QList.
    For communication between clients and server I have established a kind of ping pong request: Client: send QString("I have new datapack_1") Server: send QString("OK I'm ready for datapack_1") Client: send QByteArray(stuff).

    Everything is fine when just one client is connected. But how can I figure out, which socket is just used for connection? I cannot use sender from QTcpServer, because it is protected and my class is not derived from QTcpServer.

    Any clue out there?

    remark: only the requesting client shall be updated. Not every connected client.

    Regards
    Vater

    1 Reply Last reply
    0
    • K Offline
      K Offline
      koahnig
      wrote on last edited by
      #2

      Are your client objects on the server side already classes derived from or wrapping QTcpSocket?
      If so, you need to forward the signal received from socket, presumably readyRead(), with an indication which socket has triggered it. The simplest thing is to use the socket's address.

      Vote the answer(s) that helped you to solve your issue(s)

      1 Reply Last reply
      0
      • T Offline
        T Offline
        tucnak
        wrote on last edited by
        #3

        Init QTcpSocketList
        @
        typedef QTcpSocketList QList<QTcpSocket*>
        @

        Now add list:
        @
        private:
        QTcpSocketList list;
        @

        And on signal - newConnection():
        @
        void MyTcpServer::slotNewConnection()
        {
        list.push_back( nextPendingConnection() );
        }
        @

        It will add all connection sockets to list.

        1 Reply Last reply
        0
        • V Offline
          V Offline
          Vater Frost
          wrote on last edited by
          #4

          Not sure, if I got your points. Maybe it helps if I post a few pieces of code.

          This is the header of the Server class. It is derived from QObject to use connect. Basic idea was to include UDP and TCP in one class. You can see, that tcp_socket is a QList.

          @
          namespace nsServer{

          class Server : public QObject{
          Q_OBJECT
          private:

          bool initialized;

          QTcpServer *tcp_server;
          QList<QTcpSocket *> tcp_socket;
          QNetworkSession *network_session;
          int n_sockets;

          bool new_client_connected;

          private slots:
          void NewConnection(void);
          void DisconnectSocket(void);
          void Receive(void);
          void NetworkSessionError(QNetworkSession::SessionError _error);
          @

          The signal newConnection is triggering the following function in which each new connection/socket is stored in the QList:
          @
          //------------------------------------------------------------------------------
          void Server::NewConnection(void){
          //------------------------------------------------------------------------------

          if (!initialized) return;

          while (tcp_server->hasPendingConnections()){
          QTcpSocket *socket = tcp_server->nextPendingConnection();

            connect(socket, SIGNAL(readyRead()),    this, SLOT(Receive()));
            connect(socket, SIGNAL(disconnected()), this, SLOT(DisconnectSocket()));
            tcp_socket.append(socket);
            n_sockets++;
          

          }

          has_connection = true;

          //store connection in client_array
          QString s = (tcp_socket.at(n_sockets-1)->peerAddress().toString() + ":" +
          QString::number(tcp_socket.at(n_sockets-1)->peerPort()));
          last_client = s.toStdString();

          }
          @

          Now in my send function, called from outside server-class, I would like to identify the last connected socket triggered. I think it can be done by identifying the sender-Object from tcp_server and casting the QObject to a QTcpSocket pointer. But this is only working correctly if the class Server (i.e. this pointer) is derived from TcpServer. That's my problem.

          @
          //------------------------------------------------------------------------------
          void Server::Send(QString _string, bool _to_all){
          //------------------------------------------------------------------------------

          if (!initialized) return;

          has_received = false;

          QByteArray byte_array;
          QDataStream out(&byte_array, QIODevice::WriteOnly);
          out.setVersion(QDataStream::Qt_4_7);

          //reserve memory of first bytes
          out << (quint16)0;
          //write string into stream
          out << _string.toAscii();
          //go back to beginning of stream
          out.device()->seek(0);
          //write length of string into first bytes
          out << (quint16)(byte_array.size() - sizeof(quint16));

          if (_to_all){
          foreach(QTcpSocket *socket, tcp_socket){
          socket->write(byte_array);
          // socket->disconnectFromHost();
          }
          }
          else {
          QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
          if (!socket) return;
          socket->write(byte_array);
          }

          received_string.clear();
          received_byte_array.clear();

          int max_char;
          (_string.size() < 10)? max_char = _string.size():10;

          last_message = "String " + _string.left(max_char).toStdString() + "... sent to client.";
          sent_message = last_message;

          }
          @

          1 Reply Last reply
          0
          • K Offline
            K Offline
            koahnig
            wrote on last edited by
            #5

            @ connect(socket, SIGNAL(readyRead()), this, SLOT(Receive()));@

            All sockets are connected to the same slot. How do you determine from which socket to read?
            Are you searching every time the list to find the socket with the data received?
            However, if you have found the socket with the request, you know already the socket you have to send the answer to.

            Vote the answer(s) that helped you to solve your issue(s)

            1 Reply Last reply
            0
            • K Offline
              K Offline
              koahnig
              wrote on last edited by
              #6

              Instead of using QTcpSocket directly you could use:
              @
              class TcpSocket: public QObject
              {
              Q_OBJECT

                  QTcpSocket * Socket;
              
              public:
              
                  TcpSocket (QTcpSocket *);
                  virtual ~TcpSocket ();
              
              private slots:
              
                  void sltReadyRead ();
                  void connected ();
                  void disconnected ();
              
              signals:
              
                  void dataReady (TcpSocket * sckt );
              

              };
              @
              and the implementation
              @
              TcpSocket :: TcpSocket (QTcpSocket * socket)
              : Socket ( socket )
              {
              connect (Socket, SIGNAL (readyRead()), this, SLOT (sltReadyRead()));
              }

              void TcpSocket :: sltReadyRead ()
              {
              emit dataReady (this);
              }
              @

              You need to use this class instead of QTcpSocket in the QList and modify the connect and the receive slot.
              Your Receive slot will know then from the data was received and where to send the answer.

              Vote the answer(s) that helped you to solve your issue(s)

              1 Reply Last reply
              0
              • V Offline
                V Offline
                Vater Frost
                wrote on last edited by
                #7

                It was quite a brain teaser. I solved it now in a different way. I have deleted the handshake between client and server and replaced it with an initial QString in each byte array, which is read and used for identification of incoming byte format. In the Receive function I am identifying the used socket by use of "bytesAvailable"

                @
                //------------------------------------------------------------------------------
                void Server::ReceivePackage(void){
                //------------------------------------------------------------------------------

                if (!initialized) return;

                int i_socket = -1;
                for (int i=0;i<(int)connected_sockets.size();++i){
                if (connected_sockets.at(i)->bytesAvailable() != 0){
                i_socket = i;
                break;
                }
                }
                if (i_socket == -1) return;

                quint16 byte_array_size = 0;
                QDataStream in(connected_sockets.at(i_socket));
                in.setVersion(QDataStream::Qt_4_7);

                //Read number of bytes to read
                in >> byte_array_size;
                //check if available bytes match
                if (connected_sockets.at(i_socket)->bytesAvailable() < byte_array_size){
                PRINT("Server","Error in Receive - Number of bytes manipulated!");
                return;
                }

                //Read identifyer and pull it to QString
                in >> received_string;

                //Read rest of byte array
                received_byte_array = connected_sockets.at(i_socket)->readAll();

                has_received = true;

                last_message = (received_string + " received.").toStdString();

                }
                @

                The sending function is sending always to every socket and the clients choose by themselves by anaylsing the identifying string if it is usefull or not.

                @
                //------------------------------------------------------------------------------
                void Server::SendPackage(QByteArray _byte_array){
                //------------------------------------------------------------------------------

                if (!initialized) return;

                has_received = false;

                foreach(QTcpSocket *socket, connected_sockets){
                socket->write(_byte_array);
                }

                received_string.clear();
                received_byte_array.clear();

                last_message = "Byte array sent to all sockets.";
                sent_message = last_message;

                }
                @

                My uncertainty is still the disconnect signal, which I've solved like mentioned in a help blog from qt by using qobject_cast. I do not know if sender() from QObject is returning the same pointer than QTcpServer. But: It works.

                @
                //------------------------------------------------------------------------------
                void Server::DisconnectSocket(void){
                //------------------------------------------------------------------------------

                QTcpSocket *client = qobject_cast<QTcpSocket *>(sender());

                if (!client)
                return;

                connected_sockets.removeAll(client);
                client->deleteLater();
                n_sockets--;
                }
                @

                readyRead signal pointing to NewConnection is solved with the while loop.
                @
                //------------------------------------------------------------------------------
                void Server::NewConnection(void){
                //------------------------------------------------------------------------------

                if (!initialized) return;

                while (tcp_server->hasPendingConnections()){
                QTcpSocket *socket = tcp_server->nextPendingConnection();

                  connect(socket, SIGNAL(readyRead()),    this, SLOT(ReceivePackage()));
                  connect(socket, SIGNAL(disconnected()), this, SLOT(DisconnectSocket()));
                  connected_sockets.append(socket);
                  n_sockets++;
                

                }

                new_client_connected = true;

                //store connection in client_array
                QString s = (connected_sockets.at(n_sockets-1)->peerAddress().toString() + ":" +
                QString::number(connected_sockets.at(n_sockets-1)->peerPort()));
                last_client = s.toStdString();

                }
                @

                It is still a bit buggy. However it is much more efficient using the byte array with identifying string instead of handshaking messages. If you think, there is still something totally wrong, I'm happy to hear your advise.

                Thanks
                Vater

                1 Reply Last reply
                0
                • K Offline
                  K Offline
                  koahnig
                  wrote on last edited by
                  #8

                  It is depending on the number of of clients you have to serve. However, apparently, the number of clients is not high. Otherwise sending to all clients creates quite some overhead.
                  I never used sender(), so I cannot comment. For disconnecting you could go through your list and check each socket for lost connection and remove, when necessary.
                  However, when you are earmarking with the address or other means the signals as in the example I have provided in the post, you can get around these things.

                  Vote the answer(s) that helped you to solve your issue(s)

                  1 Reply Last reply
                  0

                  • Login

                  • Login or register to search.
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved