Add simple acknowledge protocol to ensure the server has received all data

The server now acknowledges every received message by sending a \r.

By waiting for the acknowledgement, clients should no longer terminate
too early, causing bytes getting lost in Qt's internal socket handling.

Message handling in the server is simplified because we can now rely
on the readyRead signal being raised for every frame.

This should finally solve #125 and #121.

What remains is to correctly handle data sent using
SingleApplication::sendMessage.
This commit is contained in:
Nils Jeisecke 2021-05-31 15:23:18 +02:00
parent fda9c4b178
commit 0c458ec8c9
3 changed files with 31 additions and 14 deletions

View File

@ -248,10 +248,7 @@ bool SingleApplication::sendMessage( const QByteArray &message, int timeout )
if( ! d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect ) ) if( ! d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect ) )
return false; return false;
d->socket->write( message ); return d->writeConfirmedMessage( timeout, message );
bool dataWritten = d->socket->waitForBytesWritten( timeout );
d->socket->flush();
return dataWritten;
} }
/** /**

View File

@ -272,11 +272,31 @@ bool SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType conne
#endif #endif
headerStream << static_cast <quint64>( initMsg.length() ); headerStream << static_cast <quint64>( initMsg.length() );
socket->write( header ); if( !writeConfirmedMessage( static_cast<int>(msecs - time.elapsed()), header ) )
socket->write( initMsg ); return false;
bool result = socket->waitForBytesWritten( static_cast<int>(msecs - time.elapsed()) );
if( !writeConfirmedMessage( static_cast<int>(msecs - time.elapsed()), initMsg ) )
return false;
return true;
}
void SingleApplicationPrivate::writeAck( QLocalSocket *sock ) {
sock->putChar('\n');
}
bool SingleApplicationPrivate::writeConfirmedMessage( int msecs, const QByteArray &msg )
{
socket->write( msg );
socket->flush(); socket->flush();
return result;
bool result = socket->waitForReadyRead( msecs ); // await ack byte
if (result) {
socket->read( 1 );
return true;
}
return false;
} }
quint16 SingleApplicationPrivate::blockChecksum() const quint16 SingleApplicationPrivate::blockChecksum() const
@ -379,9 +399,7 @@ void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
info.stage = StageBody; info.stage = StageBody;
info.msgLen = msgLen; info.msgLen = msgLen;
if ( sock->bytesAvailable() >= (qint64) msgLen ){ writeAck( sock );
readInitMessageBody( sock );
}
} }
void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock ) void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
@ -448,15 +466,15 @@ void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
Q_EMIT q->instanceStarted(); Q_EMIT q->instanceStarted();
} }
if (sock->bytesAvailable() > 0){ writeAck( sock );
this->slotDataAvailable( sock, instanceId );
}
} }
void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId ) void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
{ {
Q_Q(SingleApplication); Q_Q(SingleApplication);
Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() ); Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
writeAck( dataSocket );
} }
void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId ) void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )

View File

@ -81,6 +81,8 @@ public:
QString primaryUser() const; QString primaryUser() const;
void readInitMessageHeader(QLocalSocket *socket); void readInitMessageHeader(QLocalSocket *socket);
void readInitMessageBody(QLocalSocket *socket); void readInitMessageBody(QLocalSocket *socket);
bool writeConfirmedMessage(int msecs, const QByteArray &msg);
void writeAck(QLocalSocket *sock);
static void randomSleep(); static void randomSleep();
void addAppData(const QString &data); void addAppData(const QString &data);
QStringList appData() const; QStringList appData() const;