Reputation: 11
I am new to Qt and am in a bit of a struggle. I am trying to send a string from a client to a server using QTcpSocket.
Client side:
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << name;
tSock->connectToHost(ipAddress, portNumb.toInt());
tSock->waitForConnected();
tSock->write(block); // block is not empty
tSock->flush();
Server side:
void Server::readyRead()
{
QByteArray block;
QDataStream out(&block, QIODevice::ReadOnly);
out << tcpSocket->readAll();
QString name(block); // block is empty
players.insert(name, tcpSocket);
std::cout << "name: " << name.toStdString(); // TODO remove
}
On the server side program enters readyRead()
when data is received, but block is empty, although on the client side block is not empty when tSock->write(block)
is called... Where seems to be the problem? I would appreciate any help. Thanks!
Upvotes: 1
Views: 2762
Reputation: 2831
Edit: Your mistake is that you open the out
data stream in ReadOnly mode, but try to write the received byte array to it:
void Server::readyRead()
{
QByteArray block;
QDataStream out(&block, QIODevice::ReadOnly); // !mistake, WriteOnly mode is needed
out << tcpSocket->readAll(); // this is write operation
//...
}
Additional: please note that there is the Serialization mechanism of Qt Data Types which is useful in such cases:
tSock->write(block); // this is write just a raw data of the block, not the "QByteArray"
You can use a stream operation to write the necessary Qt data types to a socket directly, without a convertion to QByteArray
:
// Connect firstly
tSock->connectToHost(ipAddress, portNumb.toInt());
tSock->waitForConnected();
// Then open a data stream for the socket and write to it:
QDataStream out(tSock, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << name; // write string directly without a convertion to QByteArray
// Then you may
tSock->flush();
On Client side, and then use the similar stream operation on Server side:
void Server::readyRead()
{
QString name;
QDataStream in(tcpSocket, QIODevice::ReadOnly /*or QIODevice::ReadWrite if necessary */);
in.setVersion(QDataStream::Qt_4_0);
in >> name; // read the string
//...
}
There is also possible to read/write objects another Qt's i/o devices: QFile, QSerialPort, QProcess, QBuffer and others.
Edit 2: it is not guaranteed that on readyRead
signal you'll receive full package that was sent. Therefore see the example below.
Please note, that in the real case (when you have several different packets in the client-server communication, and it is unknown what kind of the several possible packages you have received) usually there is used more complex algorithm, because the following situations may occur on the readyRead event in a communication:
The variant of the algorithm (Qt 4 Fortune Client Example):
void Client::readFortune() // on readyRead
{
QDataStream in(tcpSocket);
in.setVersion(QDataStream::Qt_4_0);
if (blockSize == 0) {
if (tcpSocket->bytesAvailable() < (int)sizeof(quint16))
return;
in >> blockSize;
}
if (tcpSocket->bytesAvailable() < blockSize)
return;
QString nextFortune;
in >> nextFortune;
//...
}
Qt 4.0 is too old version of Qt, therefore see also Qt 5.9 Fortune Client Example
Upvotes: 2
Reputation: 98545
The readyRead
code incorrectly fills out the block
. You're also assuming that you will read enough data to convey the entire string. This is never guaranteed. All that you know when readyRead
fires it that there is at least one byte to be read. The QDataStream
transaction system helps with that.
This would be one correct implementation:
class Server : public QObject {
Q_OBJECT
QPointer<QTcpSocket> m_socket;
QDataStream m_in{m_socket.data(), &QIODevice::ReadOnly};
void onReadyRead();
public:
Server(QTcpSocket * socket, QObject * parent = {}) :
QObject(parent),
m_socket(socket)
{
connect(socket, &QIODevice::readyRead, this, &Server::onReadyRead);
}
}
void onReadyRead() {
while (true) { // must keep reading as long as names are available
QString name;
in >> name;
if (in.commitTransaction()) {
qDebug << name;
players.insert(name, &m_socket.data());
} else
break;
}
};
Upvotes: 0
Reputation: 679
It seems like your server doen't get enough time to write the data from client. please try this..
tcpsocket->waitForBytesWritten(1000);
Upvotes: 0