Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Qt bidirectional client server using QTcpSocket and QTcpServer

I am trying to implement a bidirectional client-server program, where clients and servers can pass serialized objects between one another. I am trying to do this using Qt (QTcpSocket and QTcpServer). I have implemented programs like this in java, but I can't figure out how to do it using Qt. I've checked out the fortune client and fortune server examples...but from what I can see, the client is simply signaling the server, and the server sends it some data. I need for the client and server to send objects back and forth. I am not looking for a complete solution, all I am looking for is some guidance in the right direction.

I wrote some code, which accepts a connection, but does not accept the data.

SERVER

this class is the server; it should be accepting a connection and outputting the size of the buffer which is being sent. However it is outputting 0

#include "comms.h"

Comms::Comms(QString hostIP, quint16 hostPort)
{
    server = new QTcpServer(this);
    hostAddress.setAddress(hostIP);
    this->hostPort = hostPort;


}

void Comms::attemptConnection(){
    connect(server, SIGNAL(newConnection()), this, SLOT(connectionAccepted()));
    //socket = server->nextPendingConnection();
    server->listen(hostAddress,hostPort);
    //receivedData = socket->readAll();
}

void Comms::connectionAccepted(){
    qDebug()<<"Connected";
    socket = new QTcpSocket(server->nextPendingConnection());

    char* rec = new char[socket->readBufferSize()];
    qDebug()<<socket->readBufferSize();
}

CLIENT

This class is the client. It should be sending the string 'hello'. It sends it successfully (to my knowledge)

#include "toplevelcomms.h"
#include "stdio.h"

TopLevelComms::TopLevelComms(QString hostIP, quint16 hostPort)
{
    tcpSocket = new QTcpSocket();
    hostAddress.setAddress(hostIP);
    this->hostPort = hostPort;
}


void TopLevelComms::connect(){
    tcpSocket->connectToHost(hostAddress,hostPort,QIODevice::ReadWrite);
    //tcpSocket->waitForConnected(1);

    QString string = "Hello";
    QByteArray array;
    array.append(string);
    qDebug()<<tcpSocket->write(array);
}

Please tell me what I'm doing wrong, or tell me the general logic of establishing what I want in Qt.

like image 222
PTBG Avatar asked Jan 18 '12 21:01

PTBG


2 Answers

QTcpSocket is asynchronous by default, so when you call connectToHost and write in same context it won't be sent, as socket is not connected. You should change your "client" code:

void TopLevelComms::connect(){
    tcpSocket->connectToHost(hostAddress,hostPort,QIODevice::ReadWrite);
    if(tcpSocket->waitForConnected()) // putting 1 as parameter isn't reasonable, using default 3000ms value
    {
        QString string = "Hello";
        QByteArray array;
        array.append(string);
        qDebug()<<tcpSocket->write(array);
    }
    else
    {
        qDebug() << "couldn't connect";
    }
}

Note: you also didn't check if you're able to listen

void Comms::attemptConnection(){
    connect(server, SIGNAL(newConnection()), this, SLOT(connectionAccepted()));
    //socket = server->nextPendingConnection();

    if(server->listen(hostAddress,hostPort))
    {
        qDebug() << "Server listening";
    }
    else
    {
        qDebug() << "Couldn't listen to port" << server->serverPort() << ":" << server->errorString();
    }
    //receivedData = socket->readAll();
}

And last thing. Note that QTcpServer::nextPendingConnection() return QTcpSocket, so instead of taking that new connection you create new QTcpSocket with nextPendingConnection as parent

void Comms::connectionAccepted(){
    qDebug()<<"Connected";
    // WRONG! it will use QTcpSocket::QTcpSocket(QObject * parent)
    //socket = new QTcpSocket(server->nextPendingConnection());
    // use simple asign
    socket = server->nextPendingConnection();
    // move reading to slot
    connect(socket, SIGNAL(readyRead()), this, SLOT(readSocket()));
}

now we will move reading to separate slot

void Comms::readSocket()
{
    // note that dynamic size array is incompatible with some compilers
    // we will use Qt data structure for that
    //char* rec = new char[socket->readBufferSize()];
    qDebug()<<socket->readBufferSize();
    // note that QByteArray can be casted to char * and const char *
    QByteArray data = socket->readAll();
}

I must admit, that it is a lot of errors as for such small code sample. You need to get some knowledge about TCP/IP connections. Those are streams and there is no warranty that whole data chunk will get to you at once

like image 51
Kamil Klimek Avatar answered Nov 08 '22 13:11

Kamil Klimek


It looks like you have a timing issue. Since your client and server are different processes, there's no way you can guarantee that the entirety of TopLevelComms::connect() is being executed (along with the network data transfer) before your server's connectionAccepted() function tries to read from the socket.

I suspect that if you take advantage of QTcpSocket's waitForReadyRead() function, you should have better luck:

void Comms::connectionAccepted(){
    qDebug()<<"Connected";
    socket = new QTcpSocket(server->nextPendingConnection());

    if( socket->waitForReadyRead() ) {
        char* rec = new char[socket->readBufferSize()];
        qDebug()<<socket->readBufferSize();
    }
}
like image 24
Chris Avatar answered Nov 08 '22 15:11

Chris