Send and receive data over network using QTcpSocket and QThread



  • Hi all,
    I am developing a small network game (ping-pong) in which two players (player1 and player2) must be connected over network,
    Firstly, player1 (server) must wait for player2 (the client) to connect :
    @
    //net
    tcpServer.setMaxPendingConnections(1);
    if(!tcpServer.listen(QHostAddress::Any, 23)){
    QMessageBox::critical ( this, "Connection Error !", tcpServer.errorString());
    }

    connect(&tcpServer, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
    @
    When player2 connect, two threads will start :
    @
    void MainWindow::acceptConnection()
    {
    tcpSocket = new QTcpSocket;
    tcpSocket = tcpServer.nextPendingConnection();

    if (!tcpSocket->isValid())
    QMessageBox::critical ( this, "Connection Error !", tcpSocket->errorString());
    else
    {
    QMessageBox::information(this, "Connection",
    "Connection Established to player 2,<br/><strong style="color:red;">Ready !</strong>");

    thread1 = new Thread1(tcpSocket);
    thread2 = new Thread2;
    thread1->start();
    thread2->start();

    tcpServer.close();
    connect(tcpSocket, SIGNAL(error( QAbstractSocket::SocketError)), this,
    SLOT(connectionError(QAbstractSocket::SocketError)));
    }

    }
    @
    The thread implementation :
    @
    extern Rack * m_Rack1;
    extern Rack * m_Rack2;
    extern Ball * m_Ball;

    QQueue <qreal>queue;
    QMutex mutex;

    /*

    • This thread will send player1's racket's position and
    • the position of the ball to player 2 and will receive the position
    • of racket 2 and store it in a queue of reals;
      */

    Thread1::Thread1(QTcpSocket * socket)
    {
    tcpSocket = new QTcpSocket;
    tcpSocket = socket;
    }

    void Thread1::run()
    {
    QTimer timer;
    connect(&timer, SIGNAL(timeout()), this, SLOT(sendData()));
    timer.setInterval(10);
    timer.start();

    connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(receiveData()));

    exec();
    timer.stop();
    }

    void Thread1::sendData()
    {
    QDataStream out(tcpSocket);
    float rackPos = m_Rack1->y();
    out << rackPos;

    QPointF ballPos = m_Ball->pos();
    out << ballPos;
    }

    void Thread1::receiveData()
    {
    QDataStream in(tcpSocket);

    mutex.lock();

    qreal rackPos;
    in >> rackPos;
    queue.enqueue(rackPos);

    mutex.unlock();
    }

    Thread1::~Thread1()
    {
    delete tcpSocket;
    }

    /*

    • This second thread will set the positions of racket 2
      */

    void Thread2::run()
    {
    QTimer timer;
    connect(&timer, SIGNAL(timeout()), this, SLOT(setPosition()));
    timer.setInterval(10);
    timer.start();

    exec();
    timer.stop();
    }

    void Thread2::setPosition()
    {
    mutex.lock();

    if(!queue.isEmpty())
    {
    qreal rackPos = queue.dequeue();
    m_Rack2->setY(rackPos);
    }

    mutex.unlock();
    }
    @
    the client side (player2) code is :
    @
    void MainWindow::connectToPlayer()
    {
    tcpSocket->connectToHost(serverIp, port, QIODevice::ReadWrite);

    connect(tcpSocket, SIGNAL(connected()), this, SLOT(connectedToPlayer()));
    connect(tcpSocket, SIGNAL(error( QAbstractSocket::SocketError)), this,
    SLOT(connectionError(QAbstractSocket::SocketError)));

    }

    void MainWindow::connectedToPlayer()
    {
    QMessageBox::information ( this, "Connection", "Connection Established to player1,
    <br/><strong style="color:red;">Ready !</strong>");

    thread1 = new Thread1(tcpSocket);
    thread2 = new Thread2;

    thread1->start();
    thread2->start();
    }
    @
    exactly as player1 : thread1 will send player2's racket's position to player 1 and
    will receive the positions of the ball and racket 1 and store them in queue1 and queue2. And thread2 will set the positions of the ball and racket 1

    I used the threads to make the game works faster : while the main thread handles the positions of each player 's ball and rackets
    an other thread will send and receive data over the network to synchronize events between the two players : if player 1 moves its racket, player 2 will receive immediately the new position of the racket to make the two players work exactly as they are in the same host.

    • The problem ::
      Despite the use of threads the game still slow : when a player move its racket the other player receives late the new position !
      Is the problem in the thread use or in the socket use or it has an other cause ?


  • You create your QTimer's as local variables, so when the function returns they are automatically destroyed. Either declare them as class variables, or allocate them on the heap and give them a parent.



  • bq. You create your QTimer’s as local variables, so when the function returns they are automatically destroyed. Either declare them as class variables, or allocate them on the heap and give them a parent.

    Does it matter?
    @void Thread2::run()
    {
    QTimer timer;
    connect(&timer, SIGNAL(timeout()), this, SLOT(setPosition()));
    timer.setInterval(10);
    timer.start();

    exec();
    timer.stop();
    }@
    Timer object is destroyed after run() exits and it is also stopped before that.

    But Thread1 constructor leaks:
    @Thread1::Thread1(QTcpSocket * socket)
    {
    tcpSocket = new QTcpSocket;
    tcpSocket = socket;
    }@
    You allocate QTcpSocket to heap but then use pointer which is given as argument to constructor.
    Same problem in void MainWindow::acceptConnection()

    I would say that those threads won't speed your game, only make it more complex.

    I think I had problem with Windows and QTcpSocket when sending small pieces of data rapidly.
    Setting QAbstractSocket::LowDelayOption to 1 on client side after socket was connected fixed it.



  • Hi all,

    Terence Simpson :

    "You create your QTimer’s as local variables, so when the function returns they are automatically destroyed. Either declare them as class variables, or allocate them on the heap and give them a parent"

    I declared QTimer in the constructor but I had the some result. In addition, QTimr is declared in the loop of the thread (exec())
    which means that QTimer is declared once (when we call thread->start() !)

    Tomma Tomma:

    I would say that those threads won’t speed your game, only make it more complex.

    I developed, before, the some game without threads and I had the some problem.

    Well this is, say, a "draft code" that may contains some stupid mistakes (tcpSocket = new QTcpSocket;).


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.