PDA

View Full Version : Commands exchange between Client and Server



QAlex
20th November 2009, 13:12
Hi,
the problem I need to solve, is related to the communication between a client and a server program. Communication is based on QTcpSocket class.
The client application send some command to the server... the server execute the command and maybe gives a reply to the server (not always, it depends on the command). Anyway...
To send commands I have written a function for every command, like this:



void Client::SomeCommand()
{
cmdId = 3;

tcpSocket->abort();

tcpSocket->connectToHost(QHostAddress::LocalHost, 2070);
tcpSocket->waitForConnected(3000);
}


When the SIGNAL connected() is emitted, I execute the next slot:


void Client::sendCommand()
{
quint16 blocksize = 0;
QString reqId = "REQ";
QString command;

QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_5);

switch(cmdId)
{
[...]

case 3:
command = "EXAMPLECOMMAND";
out << blocksize << reqId << command;
out.device()->seek(0);
out << quint16(block.size() - sizeof(quint16));
tcpSocket->write(block);

break;



[...]

default:
return;
}

cmdId = 0;

}

I posted only one command to clarify the structure.
If I call one command every much time, there is no problem... But if I call two commands very near in time, the signal connected() will not be emitted before the first command is really sent, so the connection of the first command is aborted and the cmdId variable is written with the value of the second command. As a result, I obtain that only the second command is sent!!! :mad:
Any idea on how to avoid this problem? Maybe this is not the best way to send commands... Have you got some suggestions on how to write better code?

Thank you very much.
Alex

high_flyer
20th November 2009, 13:43
Why are you calling connectToHost() for each command?
Once the socket is connected, you don't need to call connectToHost() again.

QAlex
20th November 2009, 16:01
Yes... You are right!
But the server application at the moment starts a thread every connection received.
Do you think that there would be some advantages in calling connectToHost only one time?
In each case I'd have to emit a signal for each command, in order to execute the sendCommand function! Probably there would be less delay between the calling function and the sendCommand function, because the signal would be emitted immediately.

I will try the solution I have in mind, thanks to your helpful hint!!! I have to modify several things, but I think that this could be the real solution!
I will let you know the results...

Bye,
Alex :D

high_flyer
20th November 2009, 16:23
But the server application at the moment starts a thread every connection received.
That is ok, but each client needs to connect only one time.


Do you think that there would be some advantages in calling connectToHost only one time?
Yes.
See what your problem description was:

If I call one command every much time, there is no problem... But if I call two commands very near in time, the signal connected() will not be emitted before the first command is really sent....
So I think your problem and connecting has very much to do with each other.

QAlex
20th November 2009, 17:13
Ok,
now I'm planning to modify these things:

1. I call connectToHost only one time (e.g. when I send the first command from the client to server).
2. To send commands I can keep the actual structure, but instead of emitting a signal to call sendCommand(), I can call directly sendCommand(cmdId), after modifying the function... I can also eliminate cmdId variable and use a const as argument.
3. I can call the function to close the connection just before closing the program.
4. On the server software, instead of starting a new thread for every incoming connection, I can start a new thread when receiving new data (SIGNAL readyRead???). But this is dangerous, because if I receive data in different blocks (due to a problem...), I start an undefined number of different threads, each receiving a part of the total data. This is not good... So the thread must be one. Every new data received I start the thread and stop it after having received all data. This can be a better solution if it functions.

Surely before making all this changes it is a good habit to save the actual version in a .zip file... :D Don't you think?

See you next week high_flyer! Have a nice weekend!
Alex

sadjoker
21st November 2009, 16:14
I don`t understant: is there a problem to create a personal thread for every client connecting and destroy the thread when he disconnects? I have to write the same project and i`m going to do it with opening new thread for every new connection and then open separate SQL connection to the server for reading/writing data. Why you think to change that behavior with the threads?

QAlex
23rd November 2009, 08:27
I don't think that my problem is related to server side thread. Well... maybe it is possible, but in my opinion is improbable!
I'm going to change this behaviour because of the delay between the connection and the signal connected(). If this is too long, the command id can change between the first and the second command to send... Result: only the second command is sent!
If it is difficult to understand the problem, try to imagine to call 2 times the function SomeCommand() with different cmdId:

SomeCommand(cmdId =3);
SomeCommand(cmdId = 4);

The connection starts with cmdId = 3, but signal connected() arrives late... that's why the SomeCommand(cmdId=4) is executed before the function sendCommand is executed. So only the second command is sent. Is that clear now?

Tanuki-no Torigava
23rd November 2009, 11:25
Just a couple of things:
1. If you have to serve more than 1 client at a time or you have to run multiple commands simultaneously - use your own QThread reimplementation or have a look at QFuture.

2. Don't reopen the socket multiple times. Some firewalls consider that an attack (see SNORT for details).

3. A nice TCP/IP documentation is your friend. Just try to realize how it should work, compare to Qt implementation and you'll get that very fast.

Good luck.

QAlex
23rd November 2009, 16:59
I implemented the solution planned some posts ago... It seems to work well! :D
Now all commands arrive to the server application!
However initially I've seen that 2 commands very near in time were not executed... I was really going crazy!!! Luckily, I noticed that there was also a problem in the server application: if two commands arrive at the same time, the function connected to the signal readyRead() is called only one time, and so executes only the first command.
To solve this problem, I've made a control over the socket buffer (based on the method bytesAvailable()): if at the end of the first command there are other bytes in the buffer, I call the function to read commands another time! In this way I'm sure to execute all the commands received in the buffer!
I don't know if this is the best solution... Anyway, at the moment It works!!!
If at debug time I discover some problems, surely I let you know!
Thanks for all contributions! If any of you needs some more deep explanations for this problem, ask!

See you all!
Alex