TCP SOCKET
-
[quote author="code_fodder" date="1371742892"]Where is your actual QTcpSocket setup code?, I know its probably simple but post that as well let us see how its setup...[/quote]
OK , so in my server ready_read , when a player joins the game :
@
void MultiClientServer::ready_read()
{
QTcpSocket *client = qobject_cast<QTcpSocket *>(sender());listOfPlayers.append(new Player("incoming string",client));
// i passed the client socket to the Player constructor and added Player to list of Players
}
@
The Player constructor :
@
QTcpSocket *socket;Player::Player(QString message,QTcpSocket *socket)
{
this->socket = socket;
}QTcpSocket * Player::getSocket()
{
return socket;
}
@Then when I want to send data to all Players in the list :
@
foreach(Player *p,listOfPlayers){QByteArray block ;
QDataStream out(&block,QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_0);out << (quint64)0;
out << dataStr;
out.device()->seek(0);
out << (quint64)(block.size() - sizeof(quint64));p->getSocket()->write(block);
bool success = p->getSocket()->waitForBytesWritten();}
@ -
[quote author="ChrisW67" date="1371767757"]How do you read the remainder of the packet? Reading more (or fewer) bytes than you should will leave you out-of-sync for the subsequent packet.[/quote]
Hmm , well currently I have QString data(blockSize);
So should be reading exactly blockSize amount of data . But maybe QString isn't interpreting blockSize correctly ; I will check on that .
@
QDataStream in(this->sock);
in.setVersion(QDataStream::Qt_5_0);if (blockSize == 0) { if (this->sock->bytesAvailable() < (int)sizeof(quint16)) return; in >> blockSize; } if (this->sock->bytesAvailable() < blockSize) return; QString data(blockSize); in >> data;
@
-
waitForBytesWritten() waits until the bytes are written to the device or a timeout occurs, i.e. it makes no guarantees that all bytes have been sent (although typically this is true) or received. Bytes are sent in exactly the order they were added to the TCP socket.
No. One write != one read. Written blocks may be split or coalesced by the network stack in order to make the journey.
-
[quote author="ChrisW67" date="1371786433"]
No. One write != one read. Written blocks may be split or coalesced by the network stack in order to make the journey. [/quote]
OK , that's what I thought .
I think the correct way to do this on client side is this way then
(instead of in >> data ) :QByteArray ba = socket->read(blockSize);
QString data = ba;Problem is , conversion from QByteArray to QString is not working for me . data is empty ! How do I convert from ba to a QString ?
-
OK . SOLVED .
the correct client side code is :
@
QDataStream in(this->sock);
in.setVersion(QDataStream::Qt_5_0);if (blockSize == 0) { if (this->sock->bytesAvailable() < (int)sizeof(quint16)) return; in >> blockSize; }
if (this->sock->bytesAvailable() < blockSize)
return;QByteArray ba = this->sock->read(blockSize);
QDataStream in2(ba);
in2.setVersion(QDataStream::Qt_5_0);QString data;
in2 >> data;
@So basically , the blocksize is based on a ByteArray in the server . So to read it correctly on client side , use
QByteArray ba = this->sock->read(blockSize);
Since I need a QString in my code , i convert the bytearray using
a QDataStream . -
Regarding QString, you can instantiate QString directly from QByteArray... the two are quite compatible. I am not on my code-machine so I may get the syntax little wrong but something like this will work:
@
QByteArray ba = this->sock->read(blockSize);
QString qs(ba);
// or you can use one of the following:
qs.append(ba);
qs = ba;
@Note that QByteArray is auto-converted into Uni code when assigning it to a QString because QString IS unicode.
-
[quote author="code_fodder" date="1371798513"]Regarding QString, you can instantiate QString directly from QByteArray... the two are quite compatible. I am not on my code-machine so I may get the syntax little wrong but something like this will work:
@
QByteArray ba = this->sock->read(blockSize);
QString qs(ba);
// or you can use one of the following:
qs.append(ba);
qs = ba;
@[/quote]
Yeah , i thought it should be simple like that but it doesn't work ; the QString ends up empty . On the server side the QDataStream seems to insert a whole bunch of '\0' into the ByteArray . So when it comes back out on the client QString doesn't know how to convert it directly .
None of the following works in this case :
@
QString qs(ba);
qs.append(ba);
qs = ba;
@ -
Really??,....I believe you, I am just surprised :o
I am out of office until Monday :( but I will look at my code, because I do this sort of thing alot... Ill get back to you on that.
-
[quote author="code_fodder" date="1371835819"]Really??,....I believe you, I am just surprised :o
Ill get back to you on that.[/quote]Yeah , so if my QString hello is "Hello" , in the the following code , the variable block contains
'\0','\0','\0','\0','\0','\0','\0','&','\0','H','\0','e','\0','l','\0','l','\0','o'
after hello is sent to out <<@
QByteArray block ;
QDataStream out(&block,QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_0);out << (quint32)0;
out << hello;
out.device()->seek((quint32)0);out << (quint32)(block.size() - sizeof(quint32));
@So on the client side QString doesn't know how to translate
'\0','\0','\0','\0','\0','\0','\0','&','\0','H','\0','e','\0','l','\0','l','\0','o'. Instead of using QString on server side i tried putting a bytearray to out << but still same problem with multiple '\0' being prefixed .
-
Ah ok, I see what you are doing now...
So when you setup your QString what is really happening is that you are creating a "unicode" string (i.e. all values are now 16-bit and not 8-bit).
So the character 't' effectivly becomes '\0', 't'
'\0' --> '\0','\0', etc...Well, that is how you would view it through "8-bit eyes".
I have the feeling you want to take an 8-bit QByteArray and send it as QString (unicode) and then at the other end take the unicde and turn it back to QByteArray... is that about right?
This is how I turn a QString (unicode) into a QByteArray (8bit):
@
uint16_t* pUtf16 = (uint16_t *) (uncompressedMsg.data());// Now move to the source string while (*pUtf16++){}; _source = QString::fromUtf16(pUtf16).toLatin1();
@
This code takes a uint16 pointer and points it to the data area that we are interested in (this may be your QString.data()). Then it moves past any null characters (the while loop), then comes the funcky line of code that was a real pain to figure out. It creates a QString from utf16 (unicode) and then calls the "toLatin1()" function to convert it back to 8-bit QByteArray.
Is that what you are trying to do?....or maybe the other way around (8bit to 16-bit unicode)?
You have to remember that QString IS unicode and once you make it QString you have to be careful how you use it.
Does that make any sense?
If you can avoid QString for conversion or data carrying then I would and just stick to QByteArray where possible :)
Also by using QString you may be doubling your data size and not knowing about it :o -
Yeah , i would definitely prefer sending the data as QByteArray as is smaller . I will try this out , thanks much .
[quote author="code_fodder" date="1372073941"]This is how I turn a QString (unicode) into a QByteArray (8bit):
@
uint16_t* pUtf16 = (uint16_t *) (uncompressedMsg.data());// Now move to the source string while (*pUtf16++){}; _source = QString::fromUtf16(pUtf16).toLatin1();
@
[/quote]
-
No problem :)
Don't forget to add [Solved] to the title once you get it working (and you can post your solution if it differs from what I wrote).
good luck