PDA

View Full Version : How does a QTcpServer process the data coming in? (Qt 4, SuSe10, gcc v4.0.2)



freak
11th May 2006, 17:46
I have the following code in my app. All this works, i tested by telneting to the machine running the QTcpServer code on port 4567 and I get the "pending connection" message.

How do I do the following:

1. accept the connection.
2. Read the data coming through that connection.

Any help is appreciated. Thanks in advanced.



void mythread::run()
{
tcpServ = new QTcpServer;

connect(tcpServ = SIGNAL(newConnection()), this, SLOT(processConnection)));

tcpServ->listen(QHostAddress::Any, 4567);

exec();
}

void mythread::processConnection()
{
if(tcpServ->hasPendingConnections())
qDebug("Pending connection received");
}

jacek
11th May 2006, 17:49
You can retrieve a QTcpSocket object using QTcpServer::nextPendingConnection().

freak
11th May 2006, 17:55
Will I have to create a QTcpSocket everytime there's a new connection/message that the QTcpServer receives?

How would you read the data/msg coming in from the client machine in through the Socket?

Thanks in advanced

jacek
11th May 2006, 18:01
Will I have to create a QTcpSocket everytime there's a new connection/message that the QTcpServer receives?
Not exactly, QTcpServer will create it for you for every connection it receives, but you can send/receive many messages using single QTcpSocket.


How would you read the data/msg coming in from the client machine in through the Socket?
You can use QIODevice::read() and QIODevice::write() or QTextStream or QDataStream.

freak
11th May 2006, 18:04
Thanks jacek, I'll look those options up in the docs and post how it went.

Best regards.

freak
11th May 2006, 18:24
Is the QTcpSocket created by the QTcpServer::nextPendingConnection() called " VT " by any chance? I output the QTcpServer::socketDescriptor() and that's what it shows.

Best regards.

jacek
11th May 2006, 19:14
I output the QTcpServer::socketDescriptor() and that's what it shows.This method returns an integer.

freak
11th May 2006, 19:27
Ok I have the following:



void mythread::run()
{
tcpServ = new QTcpServer;
tcpSock = new QTcpSocket;

connect(tcpServ, SIGNAL(newConnection()), this, SLOT(processConnection()));
connect(tcpSock, SIGNAL(readyRead()), this, SLOT(readMessage()));

tcpServ->listen(QHostAddress::Any, 4567);

exec();
}

void mythread::processConnection()
{
tcpSock = tcpServ->nextPendingConnection();
}

void mythread::readMessage()
{
QDataStream in(tcpSock);
in.setVersion(QDataStream::Qt_4_1);

in >> message;

//where message is QString
qDebug(message);
}


I can't seem to read the messages sent from my client app. What's the best way of doing this? Thanks in advanced.

jacek
11th May 2006, 20:12
tcpSock = new QTcpSocket;
connect(tcpSock, SIGNAL(readyRead()), this, SLOT(readMessage()));
...
tcpSock = tcpServ->nextPendingConnection();
...
I can't seem to read the messages sent from my client app.
First you create a socket and connect to its signal, then you get another socket from QTcpServer. It won't work, because that new socket isn't connected to any of your slots.


What's the best way of doing this?
See the examples that come with Qt.

freak
11th May 2006, 22:17
Ok, I got the following code and now I can see the output from my client. I only see the first message though. All the rest of the messages sent from my client to the server are lost. I have to restart the client and then i get only the first mesage again.

I checked the Qt Docs and the only example I see that receives multiple messages to the server automatically is the "Broadcast Receiver"

http://doc.trolltech.com/4.1/network-broadcastreceiver-receiver-cpp.html

This is done using QUdpSocket. I tried a similar thing with QTcpSocket and apparently they don't share the same member functions.

I'm trying to do the following:

while receiving data from client
display each individual message.

Best regards.




void mythread::run()
{
tcpServ = new QTcpServer;

connect(tcpServ, SIGNAL(newConnection()), this, SLOT(processConnection()));

if(!tcpServ->listen(QHostAddress::Any, 4567))
{
qDebug(tr("Unable to start the server: %1.").arg(tcpServ->errorString()));
}
exec();
}

void mythread::processConnection()
{
QTcpSocket *clientConnection = tcpServ->nextPendingConnection();

char buf[1024];
clientConnection->readLine(buf, sizeof(buf));
qDebug(buf);
}

jacek
11th May 2006, 22:58
This is done using QUdpSocket. I tried a similar thing with QTcpSocket and apparently they don't share the same member functions.
This approach won't work with TCP, because UDP is a connectionless protocol.

In UDP you send messages (datagrams) without any connection, so from UDP's point of view each message has no relation with others --- they can even be delivered in different order, than they were sent. Your application listens for datagrams, when it gets one, it processes it and waits for another message.

In TCP you create a connection between client and server (through this connection you can send a stream of bytes and TCP guarantees that each byte will be delivered in the same order as it was sent). Therefore your program must keep track of all connections.

QTcpServer notifies you when new client connects and gives you a QTcpSocket which you can use to communicate with that client. You have to use this socket until the client disconnects, so you need some object that will handle the connection with a single client.

You can have multiple clients connected to your server at the same time and each will have its own QTcpSocket.

Here are some examples that use TCP:
http://doc.trolltech.com/4.1/network-blockingfortuneclient.html
http://doc.trolltech.com/4.1/network-fortuneserver.html
http://doc.trolltech.com/4.1/network-threadedfortuneserver.html

freak
11th May 2006, 23:30
Thanks for the information Jacek, I have been looking at fortune examples as reference but Im having problems understanding Qt's documentation. I can't see how you use the QTcpSocket created by the QTcpServer. Is my code above anywhere close to what it's supposed to be or am I just way lost? I appreciate the help.

Worst case i'll probably memorize the Fortune Server/Client sources and during the process something might click.

Best regards.

jacek
12th May 2006, 14:01
Here's an example:
class ClientHandler : public QObject
{
Q_OBJECT
public:
ClientHandler( QTcpSocket *socket ) : _socket( socket )
{
connect( _socket, SIGNAL( disconnected() ), this, SLOT( disconnected() ) );
connect( _socket, SIGNAL( readyRead() ), this, SLOT( readyRead() ) );
// ...
}

private slots:
// this method will be invoked when client disconnects
void disconnected()
{
// ...
deleteLater(); // delete ClientHandler instance
}

// this will be invoked when client sends some data (remember that you receive a stream of bytes )
void readyRead()
{
_buffer.append( _socket->readAll() );
// while there is at least one complete message in the buffer:
// parse & process the message
// remove that message from the buffer
}

private:
QTcpSocket *_socket;
QByteArray _buffer;
};

// ...

void mythread::processConnection()
{
QTcpSocket *clientConnection = tcpServ->nextPendingConnection();
if( clientConnection != 0 ) {
new ClientHandler( clientConnection );
}
}
You can also derive ClientHandler from QThread and use the blocking interface (just like in threaded fortune server example).

freak
15th May 2006, 16:17
Thanks a lot for the sample code and the easy to read comments and explanation Jacek. This really helped a lot in both getting my app working and understanding more how Qt works. I really appreciate the help. I'm sure this thread will be helpful to more people.

Best regards.

tpf80
27th November 2006, 05:09
Thanks for this example, after reading it the light bulb came on and now I finally that 2 way communication between my client and server.