m_idlePolicy = DoNothingIdlePolicy;
disconnecting = false;
+ authenticatingStarted = false;
commandsTimedOut = 0;
socket = new QUdpSocket(this);
idleTimer = new QTimer(this);
idleTimer->setSingleShot(true);
+ replyTimeoutTimer = new QTimer(this);
+ replyTimeoutTimer->setSingleShot(true);
+ QObject::connect(replyTimeoutTimer, SIGNAL(timeout()), this, SLOT(commandTimeout()));
+
m_localPort = 9001;
m_host = "api.anidb.info";
m_hostPort = 9000;
Client::~Client()
{
- foreach (CommandData *cmd, sentCommands)
+ foreach (AbstractReply *cmd, sentCommands)
{
// Send CLIENT_DESTROYED to indicate that no real reply will come.
- cmd->command->setRawReply(CLIENT_DESTROYED, "");
+ cmd->setRawReply(CLIENT_DESTROYED, "");
}
sentCommands.clear();
void Client::enterAuthenticatingState()
{
qDebug() << "Entering Authenticating State";
+ if (authenticatingStarted)
+ return;
+
+ authenticatingStarted = true;
if (m_sessionId.isEmpty())
{
void Client::doAuthenticate(bool success)
{
qDebug() << "doAuthenticate init";
+ authenticatingStarted = false;
if (success)
{
qDebug() << "success!";
replyCode = ReplyCode(replyCodeInt);
}
- CommandData *commandData = sentCommands.take(commandId);
- AbstractReply *cmd = commandData->command;
- bool controlCommand = commandData->controlCommand;
- delete commandData;
+ AbstractReply *cmd = sentCommands.take(commandId);
+
+ if (sentCommandOrder.head() == commandId)
+ {
+ sentCommandOrder.dequeue();
+
+ if (sentCommandOrder.isEmpty())
+ {
+ replyTimeoutTimer->stop();
+ }
+ else
+ {
+ // If we got the first command sent, we update the replyTimeoutTimer to count the remaining time untill timeout.
+ // This should be less than Client::UDP_API_COMMAND_TIMEOUT, but never negative.
+ int newTimeout = qBound(0, Client::UDP_API_COMMAND_TIMEOUT - sentCommands[commandId]->timeSent().secsTo(QDateTime::currentDateTime()), Client::UDP_API_COMMAND_TIMEOUT);
+ newTimeout *= 1000;
+ replyTimeoutTimer->start(newTimeout);
+qDebug() << "Starting replyTimeoutTimer" << newTimeout;
+ }
+ }
+ else
+ {
+ // If the reply is not for the first command sent,
+ // then there is no need to change the replyTimeoutTimer
+ // NOTE: even though this is linear search, the command should always be among the first few.
+ sentCommandOrder.removeOne(commandId);
+ }
+
+ bool controlCommand = cmd->controlCommand();
// Requeue command and reauthenticate if not logged in.
switch (replyCode)
enqueueControlCommand(createReply(LogoutCommand()), force);
}
-void Client::commandTimeout(const QByteArray &commandId)
+void Client::commandTimeout()
{
+ Q_ASSERT(!sentCommandOrder.isEmpty());
+
+ QByteArray commandId = sentCommandOrder.dequeue();
qDebug() << commandId << "timed out";
- if (!sentCommands.contains(commandId))
- return;
- CommandData *cmd = sentCommands.take(commandId);
+ if (!sentCommandOrder.isEmpty())
+ {
+ int newTimeout = qBound(0, Client::UDP_API_COMMAND_TIMEOUT - sentCommands[commandId]->timeSent().secsTo(QDateTime::currentDateTime()), Client::UDP_API_COMMAND_TIMEOUT);
+ newTimeout *= 1000;
+ replyTimeoutTimer->start(newTimeout);
+qDebug() << "Starting replyTimeoutTimer" << newTimeout;
+ }
+ // If it's empty thereis no need to start replyTimeoutTimer again, as there are no commands waiting.
- // Regenerate ID.
- cmd->command->setId(nextCommandId());
+ AbstractReply *cmd = sentCommands.take(commandId);
+ cmd->setId(nextCommandId());
- if (cmd->controlCommand)
- enqueueControlCommand(cmd->command);
+ if (cmd->controlCommand())
+ enqueueControlCommand(cmd);
else
- enqueueCommand(cmd->command);
-
- delete cmd;
+ enqueueCommand(cmd);
++commandsTimedOut;
if (m_sessionId.length())
datagram += "&s=" + m_sessionId;
- CommandData *cmd = new CommandData(command, commandId, controlCommand);
- QObject::connect(cmd, SIGNAL(timeout(QByteArray)), this, SLOT(commandTimeout(QByteArray)));
- sentCommands[commandId] = cmd;
+ command->setControlCommand(controlCommand);
+ command->setTimeSent();
+
+ sentCommands[commandId] = command;
+ sentCommandOrder.enqueue(commandId);
+
+ if (!replyTimeoutTimer->isActive())
+ {
+ replyTimeoutTimer->start(Client::UDP_API_COMMAND_TIMEOUT * 1000);
+qDebug() << "Starting replyTimeoutTimer" << replyTimeoutTimer->interval();
+ }
+
qDebug() << QString("SENDING datagram:\n\t%1\nto: %2 ([%3]:%4)")
.arg(datagram.constData())
Client *Client::m_instance = 0;
-CommandData::CommandData(AbstractReply *command, const QByteArray &commandId, bool controlCommand) : QObject()
-{
- this->command = command;
- this->controlCommand = controlCommand;
- this->commandId = commandId;
-
- connect(&timer, SIGNAL(timeout()), this, SLOT(timerTimeout()));
- timer.start(Client::UDP_API_COMMAND_TIMEOUT * 1000);
-}
-
-void CommandData::timerTimeout()
-{
- emit timeout(commandId);
-}
-
} // namespace AniDBUdpClient