PDA

View Full Version : qt network performance



criss
18th July 2006, 13:47
hi to all,

i'm using qt 4.1.x with vs7.1 (2003 version) on winxp.

i've coded the following: a (qtcp)server and clients over qtcpsocket. both are gui applications (because of easier displaying any data :). the server "accepts" clients, holts on-/offline status of all clients (ok, at the moment just 2), sends data from one client (lets say generator-client) to the other client (consumer-client). for each client/server connection i'm opening only one qtcpsocket with readwrite access. using a start-command the generator-client produces some double values, until the server send a stop-command. for sending/receiving data to/from a socket, i'm using a qdatastream. all send operations are finished with a flush().

now, the generator is sending every 5ms 3 double values to the server and the server forwards it to the consumer. when i run everything on one pc, everything works fine. all data are fast received.

but if i'm using for example 2 pc's (connected over a 100mbit hub/switch, nothing else), whereas the server/clients are started in different combinations on the pc's, then only 5-7 (approx all 20ms) messages are received form the server. the rest is getting lost.

what i'm or qt (also read posts about qt's performance problems) or windows is making wrong? i need all information, that is produced through my generator-client. will be the transfer using udp packages faster? should i use fewer socket-connections (one for read only, one for write only)? should i use another network package (winsock, raknet, ...) than qnetwork?

any hint would be appreciated?

regards

criss

wysota
18th July 2006, 14:11
Could we see some code? Does anything weird happen? Do those messages which are sent get to the consumer successfully? It looks like you're blocking the network flow (event queue? system buffers?) or something like that.

criss
18th July 2006, 14:40
czesz wysota :)

nothing weired is happeing, just with a "real" network it's somehow... i don't know. i've know just the server and the generator client for testing running. the server is getting the informations also to slow. i'm also thinking, that something is blocking, but i cannot see anything that causes it.

ok, here is the implementation for the generator client. this is a simple thread generating some values:


void genthread::run()
{
while(!stopgen)
{
x = (rand() % 10) - 5.0f;
y = (rand() % 10) - 5.0f;
z = (rand() % 5) - 2.5f;

emit message(tr("sending %1 %2 %3...").arg(QString("%1").arg(x), QString("%1").arg(y), QString("%1").arg(z)));
emit generated(x, y, z);

msleep(5);
}
}

and here is method, which sends the data using a qdatastream, and my readdata() method:



void posgen::sendit(double x, double y, double z)
{
ds << tr("posgen");
ds << x << y << z;
socket->flush();
}

void posgen::readdata()
{
ds >> inp;

if(inp.compare("simctrl") == 0)
{
ds << tr("modulename");
ds << tr("posgen");
}

if(inp.compare("start") == 0)
{
if(gen == NULL)
{
gen = new genthread(this);
connect(gen, SIGNAL(message(QString)), this, SLOT(addtolog(QString)));
connect(gen, SIGNAL(generated(double, double, double)), this, SLOT(sendit(double, double, double)));
gen->start();
}
else
{
addtolog("generator is still running");
}
}

if(inp.compare("stop") == 0)
{
if(gen != NULL)
{
gen->stopgenthread();
gen = NULL;
}
}

if(!inp.isEmpty())
addtolog(inp);
}


and the server receives the data as following:



void connectionhandler::readdata()
{
ds >> inp;

if(inp.compare("modulename") == 0)
{
ds >> inp;
emit modulename(inp, nr);
}
else if(inp.compare("posgen") == 0)
{
double x, y, z;
ds >> x >> y >> z;
emit forwardto("imggen", x, y, z);
emit message(tr("posgen> (%1, %2, %3)").arg(QString("%1").arg(x), QString("%1").arg(y), QString("%1").arg(z)));
}
else if(!inp.isEmpty())
emit message(tr("%1: %2").arg(QString("%1").arg(nr), QString("%1").arg(inp)));
}

the server receives posgen msg, tries to forward the message, if the right client is connected and displays the received values in its own log window. i mean, everything standard code, nothing complicated.

any idea?

krzysztof

wysota
18th July 2006, 15:03
Czesc :)

Can you show the part responsible for establishing connections between the components?

criss
18th July 2006, 15:12
ok, connection accept from the server side:



void serverhandler::processnewconnection()
{
QTcpSocket *socket = server->nextPendingConnection();
if(socket != 0)
{
cnt++;

connectionhandler *c = new connectionhandler(socket, cnt);

tostore ts;
ts.nr = cnt;
ts.name = QString("%1").arg(cnt);
ts.p = c;

clients.append(ts);

emit message(tr("|serverhandler| # clients connected: %1").arg(clients.size()));

connect(c, SIGNAL(message(QString)), this, SIGNAL(message(QString)));
connect(c, SIGNAL(disconnect(int)), this, SLOT(clientdisconnected(int)));
connect(c, SIGNAL(modulename(QString, int)), this, SLOT(clientname(QString, int)));
connect(c, SIGNAL(forwardto(QString, double, double, double)), this, SLOT(forwardto(QString, double, double, double)));
}
else
{
emit message("|serverhandler| socket problem. new connection not established.");
}
}


and here is the part from the generator client:



void posgen::connecttoserver()
{
QString address(ui.lineserveraddress->text());
int port = ui.lineserverport->text().toInt();
addtolog(tr("connecting to %1:%2...").arg(address, QString("%1").arg(port)));

socket->abort();
socket->connectToHost(address, port, QIODevice::ReadWrite);
}

wysota
18th July 2006, 15:44
Could you check if example network applications that come with Qt work fine? And could you check if the problem persists if you change the thread generating numbers to something like this:


void genthread::run(){
QTimer timer;
timer.setInterval(5);
connect(&timer, SIGNAL(timeout()), this, SLOT(generateNumbers()));
timer.start();
exec();
}
void genthread::generateNumbers(){
if(stopgen) QThread::exit();
x = (rand() % 10) - 5.0f;
y = (rand() % 10) - 5.0f;
z = (rand() % 5) - 2.5f;
emit message(tr("sending %1 %2 %3...").arg(QString("%1").arg(x), QString("%1").arg(y), QString("%1").arg(z)));
emit generated(x, y, z);
}

BTW. With such construction you can get rid of the thread at all and do everything in one thread. It's possible that it's the threading which causes trouble because you didn't have the event loop running (see QThread::exec() for details).

jacek
18th July 2006, 15:46
connect(gen, SIGNAL(message(QString)), this, SLOT(addtolog(QString)));
connect(gen, SIGNAL(generated(double, double, double)), this, SLOT(sendit(double, double, double)));
If gen is a thread that emits signals, you should force the connection type to Qt::QueuedConnection, otherwise you might have problems.

criss
19th July 2006, 10:01
the network examples (fortune, broadcast, ...) working fine locally and over lan.

ok, i've made the connects queuedconnection, i run the qtimer and entered qt's event loop. finally i omitted the genthread and put the qtimer and the generatenumbers method to my posgen class. and again, if i run everything locally (on one pc), i'm receiving all data and fast. but over lan (with a switch in between) not. ok, i've recognized a small increase in the received message count per second (9-11 messages), but the rest... :crying:

if i run the server and the client app locally, i see another weird behavior: if the server requests the generator-client to stop the generation, not all numbers are received from the server. when i start the number generation again, first the rest of the old data are received, then a portion (or rarely all) of the new data. here is a sample log fragment from the client and server. i tried to get the generated numbers on the same line, to compare it easier. at the end of the log you see, that still numbers are send, but not received.

any more hints?

log:



client log: server log:

<10:38:09> stopping sim...
<10:38:09> sending -2 3 -0.5... <10:38:09> posgen> (-2, 3, -0.5)
<10:38:09> sending -2 4 1.5... <10:38:09> posgen> (-2, 4, 1.5)
<10:38:09> sending 3 0 -1.5... <10:38:09> posgen> (3, 0, -1.5)
<10:38:09> sending -2 -3 0.5... <10:38:09> posgen> (-2, -3, 0.5)
<10:38:09> sending -4 1 1.5... <10:38:22> starting sim...
<10:38:09> sending 1 2 -0.5... <10:38:22> posgen> (-4, 1, 1.5)
<10:38:09> sending 4 1 1.5... <10:38:22> posgen> (1, 2, -0.5)
<10:38:09> sending -4 4 -0.5... <10:38:23> posgen> (4, 1, 1.5)
<10:38:09> sending 1 -2 -1.5... <10:38:23> posgen> (-4, 4, -0.5)
<10:38:09> sending 0 4 -0.5... <10:38:23> posgen> (1, -2, -1.5)
<10:38:09> sending -5 -3 -1.5... <10:38:23> posgen> (0, 4, -0.5)
<10:38:09> sending -4 2 -1.5... <10:38:23> posgen> (-5, -3, -1.5)
<10:38:09> sending -3 0 0.5... <10:38:23> posgen> (-4, 2, -1.5)
<10:38:09> sending -1 -5 -0.5... <10:38:23> posgen> (-3, 0, 0.5)
<10:38:09> sending 0 0 0.5... <10:38:23> posgen> (-1, -5, -0.5)
<10:38:09> sending -4 3 0.5... <10:38:23> posgen> (0, 0, 0.5)
<10:38:09> sending 3 -5 -1.5... <10:38:23> posgen> (-4, 3, 0.5)
<10:38:09> sending 2 0 -2.5... <10:38:23> posgen> (3, -5, -1.5)
<10:38:09> sending 0 4 -2.5... <10:38:23> posgen> (2, 0, -2.5)
<10:38:09> sending 4 -2 -2.5... <10:38:23> posgen> (0, 4, -2.5)
<10:38:09> sending -1 -3 -0.5... <10:38:23> posgen> (4, -2, -2.5)
<10:38:09> sending -3 1 -0.5... <10:38:23> posgen> (-1, -3, -0.5)
<10:38:09> sending -3 -3 -2.5... <10:38:23> posgen> (-3, 1, -0.5)
<10:38:09> sending -4 -4 -0.5... <10:38:23> posgen> (-3, -3, -2.5)
<10:38:09> sending 0 4 -1.5... <10:38:23> posgen> (-4, -4, -0.5)
<10:38:09> sending 3 -4 -0.5... <10:38:23> posgen> (0, 4, -1.5)
<10:38:09> sending 3 1 -1.5... <10:38:23> posgen> (3, -4, -0.5)
<10:38:09> sending 4 4 -0.5... <10:38:23> posgen> (3, 1, -1.5)
<10:38:09> sending 1 1 -2.5... <10:38:23> posgen> (4, 4, -0.5)
<10:38:09> sending 3 -1 -2.5... <10:38:23> posgen> (1, 1, -2.5)
<10:38:09> sending -2 2 0.5... <10:38:23> posgen> (3, -1, -2.5)
<10:38:09> stop <10:38:23> posgen> (-2, 2, 0.5)
<10:38:22> start <10:38:23> posgen> (-2, -4, -1.5)
<10:38:22> sending -2 -4 -1.5... <10:38:23> posgen> (-3, 0, -0.5)
<10:38:22> sending -3 0 -0.5... <10:38:23> posgen> (1, -3, -1.5)
<10:38:23> sending 1 -3 -1.5... <10:38:23> posgen> (1, -4, -1.5)
<10:38:23> sending 1 -4 -1.5... <10:38:23> posgen> (0, 2, 1.5)
<10:38:23> sending 0 2 1.5... <10:38:23> posgen> (3, 0, -1.5)
<10:38:23> sending 3 0 -1.5... <10:38:23> posgen> (-1, 4, 0.5)
<10:38:23> sending -1 4 0.5... <10:38:23> posgen> (-1, -2, -0.5)
<10:38:23> sending -1 -2 -0.5... <10:38:23> posgen> (0, -2, 0.5)
<10:38:23> sending 0 -2 0.5... <10:38:23> posgen> (-2, -3, -0.5)
<10:38:23> sending -2 -3 -0.5... <10:38:23> posgen> (-5, 1, 0.5)
<10:38:23> sending -5 1 0.5... <10:38:23> posgen> (-5, -4, -1.5)
<10:38:23> sending -5 -4 -1.5... <10:38:23> posgen> (4, 0, 0.5)
<10:38:23> sending 4 0 0.5... <10:38:23> posgen> (1, -2, -1.5)
<10:38:23> sending 1 -2 -1.5... <10:38:23> stopping sim...
<10:38:23> sending 0 -2 1.5... <10:38:23> posgen> (0, -2, 1.5)
<10:38:23> sending -5 -2 -1.5... <10:38:23> posgen> (-5, -2, -1.5)
<10:38:23> sending 2 4 -2.5... <10:38:23> posgen> (2, 4, -2.5)
<10:38:23> sending -2 0 -0.5... <10:38:23> posgen> (-2, 0, -0.5)
<10:38:23> sending 4 -4 -0.5...
<10:38:23> sending 1 -4 -0.5...
<10:38:23> sending -5 -4 -2.5...
<10:38:23> sending -3 -3 1.5...
<10:38:23> sending -5 0 -1.5...
<10:38:23> sending 3 -2 -1.5...
<10:38:23> sending 3 3 -1.5...
<10:38:23> sending -4 1 -1.5...
<10:38:23> sending 2 -5 1.5...
<10:38:23> sending 4 3 1.5...
<10:38:23> sending -1 4 0.5...
<10:38:23> sending 2 0 -1.5...
<10:38:23> sending -1 3 -1.5...
<10:38:23> sending 3 2 0.5...
<10:38:23> sending -5 4 -2.5...
<10:38:23> sending 1 4 -2.5...
<10:38:23> sending -5 -4 0.5...
<10:38:23> sending 0 0 0.5...
<10:38:23> sending -5 -4 -0.5...
<10:38:23> sending 2 -1 -1.5...
<10:38:23> sending 2 -5 -0.5...
<10:38:23> sending -1 -4 -0.5...
<10:38:23> sending -4 3 0.5...
<10:38:23> sending 0 2 -1.5...
<10:38:23> sending -1 2 -1.5...
<10:38:23> sending 3 -3 0.5...
<10:38:23> sending -5 -3 -0.5...
<10:38:23> sending 3 3 -2.5...
<10:38:23> sending 4 2 -1.5...
<10:38:23> sending -2 -1 -0.5...
<10:38:23> sending -4 -2 -0.5...
<10:38:23> sending -4 -3 -0.5...
<10:38:23> stop

nouknouk
19th July 2006, 11:49
Hi,

I didn't had enough time to check how TCP sockets are configured by default in qt, but TCP socket can use by default an optimization (its name is nagle algorithm (http://tangentsoft.net/wskfaq/intermediate.html#nagle-desc)) in order to avoid sending too much little packets and for automatically merging them. A sample :

- you send 100 packets of 3 bytes each second. As a TCP header is around 5x32 bits (http://www.networksorcery.com/enp/protocol/tcp.htm) ( = 20 bytes), and an IP header is around 5x32 bits (http://www.erg.abdn.ac.uk/users/gorry/course/inet-pages/ip-packet.html) ( = 20 bytes) you will send 100 * (20 + 20 + 3) bytes per second = 4300 bytes / second.

The optimization of the TCP layer is to wait a little bit before sending a packet, if in the next few milliseconds another bloc of data has to be sent, it will merge the two blocs of data into only one and send them in only one packet.
In the sample above, imagine all the 100 packets are merged in only one packet, you will have to send (20+20) + 3*100 = 340 bytes / second.

The problem with this optimization is that for sepcific cases (like yours) you rather prefer to increase the bandwith needed in order to lower the transmission delay.
A flag exists in order to switch of this optimization. It is called TCP_NO_DELAY.

As far as I know, you cannot choose to enable or disable this flag in Qt directly. To my mind (and if it's really because of that), you will have to write platform dependent code and call OS specific functions to set the flag.

For example, here is an example on how to disable TCP_NO_DELAY on unix platforms :


/* Disable the Nagle (TCP No Delay) algorithm ON UNIX PLATFORMS */
int sockID, flag, ret;
sockID = yourQTcpSocket->socketDescriptor();
flag = 1;
ret = setsockopt( sockID, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag) );

if (ret == -1) {
// error while trying to disable the nagle optimization
}


For windows platforms, according to this article on msdn (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/setsockopt_2.asp), it seems to be :



/* Disable the Nagle (TCP No Delay) algorithm ON WINDOWS PLATFORMS */
SOCKET sockId = yourQTcpSocket->socketDescriptor();
int ret;
BOOL bOptVal = true; // seems to be true here, but not completely sure.
ret = setsockopt(sockID, IPPROTO_TCP, TCP_NODELAY, (char*)&bOptVal, sizeof(bool))

if ( ret == SOCKET_ERROR) {
// error while trying to disable the nagle optimization
int error = WSAGetLastError(void);
}


My 2 cents.

wysota
19th July 2006, 13:19
I didn't had enough time to check how TCP sockets are configured by default in qt, but TCP socket can use by default an optimization (its name is nagle algorithm (http://tangentsoft.net/wskfaq/intermediate.html#nagle-desc)) in order to avoid sending too much little packets and for automatically merging them.

(...)

The optimization of the TCP layer is to wait a little bit before sending a packet, if in the next few milliseconds another bloc of data has to be sent, it will merge the two blocs of data into only one and send them in only one packet.
In the sample above, imagine all the 100 packets are merged in only one packet, you will have to send (20+20) + 3*100 = 340 bytes / second.

The problem with this optimization is that for sepcific cases (like yours) you rather prefer to increase the bandwith needed in order to lower the transmission delay.
A flag exists in order to switch of this optimization. It is called TCP_NO_DELAY.

It's not that. Flushing the socket causes setting the PUSH flag on the TCP segment which makes the TCP implementation send the segment as soon as possible (when the congestion allows it) and not doing any delayed optimisations like segment merging. And even if the push flag wasn't set, the data would still be sent some miliseconds later, it can't be held in the queue for too long.

nouknouk
19th July 2006, 14:24
Flushing the socket causes setting the PUSH flag on the TCP segment which makes the TCP implementation send the segment as soon as possible

Could you please tell me where did you find those informations, I'm really interrested.

Thanks.

wysota
19th July 2006, 14:36
It's surely in RFC793 (ftp://ftp.rfc-editor.org/in-notes/rfc793.txt) and follow-ups.

jacek
19th July 2006, 15:25
void connectionhandler::readdata()
{
ds >> inp;

if(inp.compare("modulename") == 0)
{
ds >> inp;
emit modulename(inp, nr);
}
else if(inp.compare("posgen") == 0)
{
double x, y, z;
ds >> x >> y >> z;
emit forwardto("imggen", x, y, z);
emit message(tr("posgen> (%1, %2, %3)").arg(QString("%1").arg(x), QString("%1").arg(y), QString("%1").arg(z)));
}
else if(!inp.isEmpty())
emit message(tr("%1: %2").arg(QString("%1").arg(nr), QString("%1").arg(inp)));
}
A small question: what happens when you receive more than one message? You always read only 3 values.

In TCP you receive a stream of bytes and there is no distinction between messages/packets. It's your job to check if you have received a whole message, part of it, or even more than one message.

http://www.qtcentre.org/forum/f-newbie-4/t-how-does-a-qtcpserver-process-the-data-coming-in-qt-4-suse10-gcc-v402-2191.html

criss
19th July 2006, 16:09
what i'm trying to realize is: first receiving a qstring (e.g. modulename, posgen, ...) to know, what kind of data will come and then accepting the data. for example, first is "posgen" received, then i know, that 3 double values will come.

i know and read the posting you send... so you mean, in the way i'm trying to receive data, there will be "problems" while receiving. this means, i have to read everything to a buffer and then parsing the buffer to extract all of my needed data?? so the qdatastream is not enough? is this correct?

jacek
19th July 2006, 16:28
i know and read the posting you send... so you mean, in the way i'm trying to receive data, there will be "problems" while receiving. this means, i have to read everything to a buffer and then parsing the buffer to extract all of my needed data?? so the qdatastream is not enough? is this correct?
Not necessarily, just readyRead() signal means that there are new data available, but it doesn't say how much data. You might receive only a part of a message or more than one message. If you won't read all available data (except for the incomplete message), they will stay in the buffer forever and it will look like client was receiving less data than server sends.

wysota
19th July 2006, 17:31
It could be easier for you if you separate each message with a new line character and use a textstream instead of datastream. Then you can check if there are more lines to read and read line by line. Of course you can try to do simmilar things using QDataStream::atEnd(), but you'll probably have to use it quite often (before every read).

criss
24th July 2006, 10:23
now i had time to finish that part and it worked. thanks again.