PDA

View Full Version : synching client readings to server output



OnionRingOfDoom
28th January 2006, 01:19
I have a server program that writes data to datastream, and writes that to the connected client. When the client recieves the readReady() signal, it reads the data the server sent. Now, whenever I move a slider on the server app, the value of that slider is transmitted to the client. The client reads that data and moves an identical slider in the client app. When I move the server slider, the client's slider moves too, but not at the same rate I moved the server slider. It always is a bit behind, and the more I move the server slider, the farther behnd the client slider gets. Is there any way I can synch the two up, so the client doesn't eventually get several minutes worth of data behind the server?

wysota
28th January 2006, 01:40
Strictly saying -- no. General answer -- depends. We'd have to see your code to suggest something as it depends how you implemented the transmission.

OnionRingOfDoom
28th January 2006, 01:47
The server sending function:


void MainWindow::sendSpeed(int amp)
{
QByteArray block;
QDataStream out(&block, QIODevice::ReadWrite);

value = 255;
out << value;

// Send the linear slider value to the client
label->setNum(amp);
clientConnection->write(block);
statusBar->showMessage("Writing Data...", 100);
}


The client reading function:


void MainWindow::readSpeed()
{
QDataStream in(&tcpClient);
in.setVersion(QDataStream::Qt_4_0);

quint8 sliderValue;
in >> sliderValue;
if (sliderValue == 0)
return;

slider->setAmpValue(sliderValue);
label->setNum(sliderValue);
}


Pretty much how it works is that the valueChanged() signal from the slider is connected to the sendSpeed() slot, and tracking is turned on for the slider. The client's readyRead() signal is connected to the client's readSpeed() slot

Codepoet
28th January 2006, 13:18
Don't send every change, or even wait till the user stopped sliding if possible.

If you want more than a smooth slider, like a fast paced game would require, I can describe you another approach that's much work but yields better results.

wysota
28th January 2006, 13:44
You could check if there are more than one message waiting to be read, and if so, ignore all but the last one. And of course send messages only when the slider reaches its final position as already suggested.

BTW. I think you should use a UDP socket here instead of TCP. It's not as reliable as TCP but will do here and you won't get those delays resulting from a possible retransmission. Of course the message could be lost, so think about it.

OnionRingOfDoom
28th January 2006, 14:20
Well, I want it to transmit all the slider values, it's kinda supposed to be a real-time stream of the slider values, so if I move my slider, I want the client slider to move the same way. Maybe that smooth slider method would work better. How would I go about doing that, exactly?

Codepoet
28th January 2006, 14:56
First you need to implement above UDP a special protocol to transmit reliable und normal messages (unreliable). In your case we give all packets a unique increasing ID to ensure that packets that arrive out of order / late are discarded. We need this because UDP does only guarantee that if a packet arrives its contents are undamaged.

Instead of implementing this yourself you could use a library which does this part for you, but then you would not use the Qt classes. There are HawkTL, OpenTNL, ENet and RakNet to name a few in no specific order.

In the server you would do the following: Every time the silder value is changed imediately send an unreliable message to the client and again while sliding every x seconds a reliable message with the current state. A final reliable message after the user stopped sliding is also a good idea.
If this produces to much unreliable packets send only every n-th change to the client.
The client receives now the messages in any order and discards messages older than the highest ID it until now received. Since nearly all unreliable UDP messages may get lost we can only hope that after several tries a reliable message reaches the client to set the current state (will only happen with bad connections).

That's the network part. We can now do some educated guesses based on prior information. When the user moves the slider very fast to one side we could guess that the slider will move a bit further than the current position. So your client could move the slider in a smooth way a bit further using interpolation between the last known state and the predicted new state. You could do this arbitraryly complex ;)

OnionRingOfDoom
28th January 2006, 17:06
That's uh, really pretty complicated. I kinda want it to be a bit more reliable then UDP, so I think I'll stick with TCP. If I only send like, every 3rd slider change value, will that be better? Also, how would I check to see how many items are in the que to be read, and get the latest one?

It's kinda like it doesn't read all the data it's recieving. The client slider moves whenever I move the server slider, but it moves a lot slower than I'd like. It also stops when I stop moving my slider, but it doesn't finish going to where the server slider was when it stopped.

Codepoet
28th January 2006, 18:11
With 3 you have only around 1/3 of the packets, with 4 around 1/4... Try some values to find what suites you best.
n = 3:
But don't forget to send the state when the user stopped sliding otherwise your client will in 2/3 of the cases not have the final value.
Are you already sending only every 3rd change? If yes that may be the problem.

You can't get the newest value directly, you have to read everything from the stream (I don't know the Qt specifics here - maybe there is a method to ignore m bytes). Just read till the end of the stream discarding all old values and only using the newest to set the slider in the client.

You can improve the TCP performance (in terms of lag) by setting certain flags: Disable Nagle's algorithm.

OnionRingOfDoom
28th January 2006, 18:33
Would it have anything to do with the fact that after each valid value read by the client, it reads an additional 0? I had to set it to ignore all values of 0 for some reason...

jacek
28th January 2006, 19:03
Would it have anything to do with the fact that after each valid value read by the client, it reads an additional 0? I had to set it to ignore all values of 0 for some reason...
What type is that value variable of (the one you use in MainWindow::sendSpeed())?

Codepoet
28th January 2006, 19:04
Your code above is incomplete (and can't work: see lines 6 + 7 of the server). Please post the current version.

As a general rule: Everything which you write into a stream you have to read back later in exactly the same way. Failure to do so corrupts your stream.
Those extra values you read are probably the reason why it does not work. And why is 0 no valid value for a slider?

jacek
28th January 2006, 19:07
void MainWindow::sendSpeed(int amp)
{
// QByteArray block; --- you don't need this
QDataStream out( clientConnection );

// Send the linear slider value to the client
label->setNum(amp);
value = 255;
out << value;
statusBar->showMessage("Writing Data...", 100);
}

OnionRingOfDoom
28th January 2006, 19:08
Jacek: the value is of the type quint 8.

Codepoet: 0 isn't a valid value because whenever the client reads 0 is imediatly after it reads the actual value, like if it reads 8, then immediatly after that it'll read a 0, for some reason. How do I get rid of those extra values? Oh... maybe if I set it to read every other time...

OnionRingOfDoom
28th January 2006, 19:15
Jacek, your fix there works perfectly! Thanks!! :D