add queue and "serialize" transfer

This commit is contained in:
Oliver Groß 2009-04-23 10:15:30 +02:00
parent bc21dae968
commit 005f20bfa9
6 changed files with 187 additions and 180 deletions

View File

@ -28,8 +28,8 @@
#define DESCRIPTOR_STRING "QFTH" #define DESCRIPTOR_STRING "QFTH"
namespace qftrans { namespace qftrans {
enum TransferStatus {TS_WAITING = 0, TS_ANNOUNCING = 1, TS_TRANSFERING = 2, TS_CLOSING = 3, TS_FINISHED = 4}; enum TransferStatus {TS_SUSPENDED = 0, TS_PENDING = 1, TS_ANNOUNCING = 2, TS_WAITING = 3, TS_TRANSFERING = 4, TS_CLOSING = 5, TS_FINISHED = 6};
enum HeaderType {HT_ID = 0, HT_SIZE = 1, HT_NAME = 2, HT_DATA = 3, HT_CLOSE = 4, HT_ACK = 5, HT_CANCEL = 6}; enum HeaderType {HT_ANNOUNCE = 0, HT_DATA = 1, HT_CLOSE = 2, HT_ACK = 3, HT_CANCEL = 4};
struct TransferData { struct TransferData {
QString fileDir; QString fileDir;
@ -39,6 +39,7 @@ namespace qftrans {
qint64 size; qint64 size;
qint64 transfered; qint64 transfered;
HeaderType lastHeader; HeaderType lastHeader;
quint32 id;
}; };
struct TransferHeader { struct TransferHeader {

View File

@ -45,8 +45,8 @@ namespace qftrans {
m_ActAsServer(false), m_ActAsServer(false),
m_Connected(false), m_Connected(false),
m_DestinationDir(QDir::homePath()), m_DestinationDir(QDir::homePath()),
m_TransferThread(NULL), m_TransferThread(NULL)
m_SelectedTransfer(NULL) // m_SelectedTransfer(NULL)
{ {
setupUi(); setupUi();
} }
@ -71,8 +71,8 @@ namespace qftrans {
newUpload->fileName = fileInfo.fileName(); newUpload->fileName = fileInfo.fileName();
newUpload->size = fileInfo.size(); newUpload->size = fileInfo.size();
newUpload->transfered = 0; newUpload->transfered = 0;
newUpload->status = TS_WAITING; newUpload->status = TS_SUSPENDED;
newUpload->lastHeader = HT_ID; newUpload->lastHeader = HT_ANNOUNCE;
newUpload->localFile = true; newUpload->localFile = true;
addTransfer(newUpload); addTransfer(newUpload);
@ -130,15 +130,28 @@ namespace qftrans {
} }
void CTransferManager::restartSelected() { void CTransferManager::restartSelected() {
emit uploadAddRequested(m_SelectedTransfer); QModelIndexList selectedIndexes = m_TransferView->selectionModel()->selectedIndexes();
emit uploadRestartRequested(m_SelectedTransfer);
foreach (QModelIndex i, selectedIndexes) {
emit uploadAddRequested(static_cast<TransferData *>(i.internalPointer()));
}
// emit uploadAddRequested(m_SelectedTransfer);
} }
void CTransferManager::cancelSelected() { void CTransferManager::cancelSelected() {
if (m_SelectedTransfer->localFile) QModelIndexList selectedIndexes = m_TransferView->selectionModel()->selectedIndexes();
foreach (QModelIndex i, selectedIndexes) {
if (static_cast<TransferData *>(i.internalPointer())->localFile)
emit uploadRemoveRequested(static_cast<TransferData *>(i.internalPointer()));
else
emit downloadRemoveRequested(static_cast<TransferData *>(i.internalPointer()));
}
/* if (m_SelectedTransfer->localFile)
emit uploadRemoveRequested(m_SelectedTransfer); emit uploadRemoveRequested(m_SelectedTransfer);
else else
emit downloadRemoveRequested(m_SelectedTransfer); emit downloadRemoveRequested(m_SelectedTransfer);*/
} }
inline void CTransferManager::setupUi() { inline void CTransferManager::setupUi() {
@ -181,6 +194,7 @@ namespace qftrans {
currentMenu->addAction(tr("About &Qt"), qApp, SLOT(aboutQt())); currentMenu->addAction(tr("About &Qt"), qApp, SLOT(aboutQt()));
m_TransferView = new QListView(this); m_TransferView = new QListView(this);
m_TransferView->setSelectionMode(QAbstractItemView::ExtendedSelection);
setCentralWidget(m_TransferView); setCentralWidget(m_TransferView);
QTransferListModel * listModel = new QTransferListModel(&m_Data); QTransferListModel * listModel = new QTransferListModel(&m_Data);
@ -196,8 +210,7 @@ namespace qftrans {
updateConnectionState(false); updateConnectionState(false);
m_ConnectionDialog = new CConnectionDialog(this); m_ConnectionDialog = new CConnectionDialog(this);
}
}
void CTransferManager::updateConnectionState(bool connected) { void CTransferManager::updateConnectionState(bool connected) {
m_Connected = connected; m_Connected = connected;
@ -231,7 +244,7 @@ namespace qftrans {
connect(worker, SIGNAL(uploadUpdated(TransferData *)), model, SLOT(updateEntry(TransferData *))); connect(worker, SIGNAL(uploadUpdated(TransferData *)), model, SLOT(updateEntry(TransferData *)));
connect(this, SIGNAL(uploadAddRequested(TransferData *)), worker, SLOT(addUpload(TransferData *))); connect(this, SIGNAL(uploadAddRequested(TransferData *)), worker, SLOT(addUpload(TransferData *)));
connect(this, SIGNAL(uploadRestartRequested(TransferData *)), worker, SLOT(startUpload(TransferData *))); // connect(this, SIGNAL(uploadRestartRequested(TransferData *)), worker, SLOT(startUpload(TransferData *)));
connect(this, SIGNAL(uploadRemoveRequested(TransferData *)), worker, SLOT(removeUpload(TransferData *))); connect(this, SIGNAL(uploadRemoveRequested(TransferData *)), worker, SLOT(removeUpload(TransferData *)));
connect(this, SIGNAL(downloadRemoveRequested(TransferData *)), worker, SLOT(removeDownload(TransferData *))); connect(this, SIGNAL(downloadRemoveRequested(TransferData *)), worker, SLOT(removeDownload(TransferData *)));
@ -239,13 +252,16 @@ namespace qftrans {
void CTransferManager::handleSelectionChanged(const QItemSelection & selected) { void CTransferManager::handleSelectionChanged(const QItemSelection & selected) {
QModelIndexList selectedIndexes = selected.indexes(); QModelIndexList selectedIndexes = selected.indexes();
if (selectedIndexes.isEmpty()) // if (selectedIndexes.isEmpty())
m_SelectedTransfer = NULL; // m_SelectedTransfer = NULL;
else // else
m_SelectedTransfer = static_cast<TransferData *>(selectedIndexes[0].internalPointer()); // m_SelectedTransfer = static_cast<TransferData *>(selectedIndexes[0].internalPointer());
//
m_TransferRestartAction->setDisabled(selectedIndexes.isEmpty());
m_TransferCancelAction->setDisabled(selectedIndexes.isEmpty());
m_TransferRestartAction->setEnabled(m_SelectedTransfer && m_SelectedTransfer->localFile); /* m_TransferRestartAction->setEnabled(m_SelectedTransfer && m_SelectedTransfer->localFile);
m_TransferCancelAction->setEnabled(m_SelectedTransfer && m_SelectedTransfer->status != TS_FINISHED); m_TransferCancelAction->setEnabled(m_SelectedTransfer && m_SelectedTransfer->status != TS_FINISHED);*/
} }
void CTransferManager::addTransfer(TransferData * transfer) { void CTransferManager::addTransfer(TransferData * transfer) {

View File

@ -85,7 +85,7 @@ namespace qftrans {
QListView * m_TransferView; QListView * m_TransferView;
TransferData * m_SelectedTransfer; // TransferData * m_SelectedTransfer;
void setupUi(); void setupUi();

View File

@ -38,44 +38,59 @@ namespace qftrans {
//TODO addUpload //TODO addUpload
void CWorker::addUpload(TransferData * transfer) { void CWorker::addUpload(TransferData * transfer) {
if (!m_UploadDict.contains(transfer)) { transfer->status = TS_PENDING;
if (!m_Uploads.contains(transfer)) {
qDebug() << "adding upload"; qDebug() << "adding upload";
transfer->status = TS_WAITING; transfer->id = m_IdCounter;
m_UploadDict.insert(transfer, m_IdCounter);
m_IdCounter++; m_IdCounter++;
m_Uploads.enqueue(transfer);
}
else
qDebug() << "(re)adding upload";
if (m_Socket->bytesToWrite() == 0) {
qDebug() << "executing upload";
writeData();
} }
} }
//TODO removeUpload //TODO removeUpload
void CWorker::removeUpload(TransferData * transfer) { void CWorker::removeUpload(TransferData * transfer) {
if (m_UploadDict.contains(transfer) && transfer->status != TS_WAITING) { if (m_Uploads.contains(transfer) && transfer->status > TS_PENDING) {
transfer->status = TS_CLOSING; transfer->status = TS_CLOSING;
if (m_Socket->bytesToWrite() == 0) { if (m_Socket->bytesToWrite() == 0) {
qDebug() << "executing upload"; qDebug() << "executing closedown";
writeData();
}
}
}
//TODO startUpload
void CWorker::startUpload(TransferData * transfer) {
if (m_UploadDict.contains(transfer) && transfer->status == TS_WAITING) {
qDebug() << "scheduling upload for announce";
transfer->status = TS_ANNOUNCING;
if (m_Socket->bytesToWrite() == 0) {
qDebug() << "executing upload";
writeData(); writeData();
} }
} }
} }
//TODO ? startUpload
// void CWorker::startUpload(TransferData * transfer) {
// if (m_Uploads.contains(transfer) && transfer->status != TS_PENDING)
// transfer->status = TS_PENDING;
//
// if (m_Socket->bytesToWrite() == 0) {
// qDebug() << "executing upload";
// writeData();
// }
// }
void CWorker::startUploadData(quint32 id) { void CWorker::startUploadData(quint32 id) {
TransferData * transfer = m_UploadDict.key(id); if (m_Uploads.isEmpty())
if (transfer) { return;
qDebug() << "scheduling upload for transfer";
transfer->status = TS_TRANSFERING; TransferData * transfer = m_Uploads.head(); /*m_UploadDict.key(id);*/
if (m_Socket->bytesToWrite() == 0) {
qDebug() << "executing upload"; if (transfer->id != id) {
writeData(); qDebug() << "got invalid id:" << id << "... expected:" << transfer->id;
} return;
}
qDebug() << "scheduling upload for transfer";
transfer->status = TS_TRANSFERING;
if (m_Socket->bytesToWrite() == 0) {
qDebug() << "executing upload";
writeData();
} }
} }
@ -85,7 +100,6 @@ namespace qftrans {
out.setVersion(QDataStream::Qt_4_0); out.setVersion(QDataStream::Qt_4_0);
TransferHeader header; TransferHeader header;
// header.descriptor = DESCRIPTOR_STRING;
header.type = HT_ACK; header.type = HT_ACK;
header.id = id; header.id = id;
@ -109,141 +123,114 @@ namespace qftrans {
} }
void CWorker::writeData() { void CWorker::writeData() {
if (m_UploadDict.isEmpty()) { if (m_Uploads.isEmpty()) {
// if (m_UploadDict.isEmpty()) {
qDebug() << "no more uploads."; qDebug() << "no more uploads.";
return; return;
} }
TransferData * transfer = m_Uploads.head();
if (transfer->status == TS_PENDING)
transfer->status = TS_ANNOUNCING;
QByteArray block; QByteArray block;
QDataStream out(&block, QIODevice::ReadWrite); QDataStream out(&block, QIODevice::ReadWrite);
out.setVersion(QDataStream::Qt_4_0); out.setVersion(QDataStream::Qt_4_0);
QTransferDataList transfers = m_UploadDict.keys(); TransferHeader header;
QTransferDataList::iterator i; switch (transfer->status) {
for (i = transfers.begin(); i != transfers.end(); ++i) { case TS_ANNOUNCING:
block.clear(); {
qDebug() << "announcing" << transfer->id;
TransferHeader header; if (transfer->transfered)
switch ((*i)->status) { transfer->transfered = 0;
case TS_ANNOUNCING: {
qDebug() << "announcing" << m_UploadDict[*i]; qint64 oldPosition = block.size();
if ((*i)->transfered)
(*i)->transfered = 0; //fake write filename
out << transfer->fileName;
// ID header with no data out.device()->seek(oldPosition);
header.type = HT_ID;
header.id = m_UploadDict[*i]; // ANNOUNCE header with size and filename
header.length = 0; header.type = HT_ANNOUNCE;
header.id = transfer->id;
out.writeRawData((const char *)(&header), sizeof(TransferHeader)); header.length = quint32(block.size() - oldPosition) + sizeof(qint64);
//size header with size
header.type = HT_SIZE;
header.length = sizeof(qint64);
out.writeRawData((const char *)(&header), sizeof(TransferHeader));
qDebug() << "sending size" << (*i)->size;
out << (*i)->size;
//name header with file name
qint64 oldPosition = block.size();
//fake write filename
out << (*i)->fileName;
out.device()->seek(oldPosition);
header.type = HT_NAME;
header.length = quint32(block.size() - oldPosition);
// qDebug() << "pos" << out.device()->pos() << block.size();
out.writeRawData((const char *)(&header), sizeof(TransferHeader));
// qDebug() << "pos post header" << out.device()->pos() << block.size();
// qint64 oldPosition = block.size() - sizeof(quint32);
qDebug() << "sending filename" << (*i)->fileName;
out << (*i)->fileName;
// qDebug() << "pos post filename" << out.device()->pos() << block.size();
// out.device()->seek(oldPosition);
// qDebug() << "pos post seek" << out.device()->pos();
/* qDebug() << "filename data length" << header.length;
out << header.length;
out.device()->seek(oldPosition);
out >> header.length;
qDebug() << "written length" << header.length;
out.device()->seek(block.size());
qDebug() << "pos post length correct" << out.device()->pos() << block.size();*/
(*i)->status = TS_WAITING;
}
break;
case TS_TRANSFERING: {
// qDebug() << "transfering" << m_DataDict[*i];
header.type = HT_DATA;
header.id = m_UploadDict[*i];
if ((*i)->transfered + DATA_BLOCK_SIZE < (*i)->size)
header.length = DATA_BLOCK_SIZE;
else
header.length =(*i)->size - (*i)->transfered;
out.writeRawData((const char *)(&header), sizeof(TransferHeader));
char * buffer = new char[header.length];
QFile file((*i)->fileDir + QDir::separator() + (*i)->fileName);
file.open(QIODevice::ReadOnly);
QDataStream in(&file);
if (in.skipRawData((*i)->transfered) == -1) {
qDebug() << "filename" << file.fileName();
qDebug() << "skip error";
qDebug() << "file size" << (*i)->size;
qDebug() << "transfered bytes" << (*i)->transfered;
}
// in.device()->seek((*i)->transfered);
if (in.readRawData(buffer, header.length) == -1) {
qDebug() << "Read error";
qDebug() << "buffer size" << header.length;
qDebug() << "file size" << (*i)->size;
qDebug() << "transfered bytes" << (*i)->transfered;
}
out.writeRawData((const char *)(buffer), header.length);
(*i)->transfered += header.length;
delete[] buffer;
if ((*i)->transfered >= (*i)->size)
(*i)->status = TS_CLOSING;
if ((*i)->transfered > (*i)->size) {
qDebug() << "overread?";
qDebug() << "file size" << (*i)->size;
qDebug() << "transfered bytes" << (*i)->transfered;
}
emit uploadUpdated(*i);
}
break;
case TS_CLOSING:
header.type = HT_CLOSE;
header.id = m_UploadDict[*i];
header.length = 0;
out.writeRawData((const char *)(&header), sizeof(TransferHeader)); out.writeRawData((const char *)(&header), sizeof(TransferHeader));
(*i)->status = TS_FINISHED; qDebug() << "sending size" << transfer->size;
out << transfer->size;
m_UploadDict.remove(*i); qDebug() << "sending filename" << transfer->fileName;
break; out << transfer->fileName;
default:
break; transfer->status = TS_WAITING;
} }
if (!block.isEmpty()) break;
m_Socket->write(block); case TS_TRANSFERING:
{
header.type = HT_DATA;
header.id = transfer->id;
if (transfer->transfered + DATA_BLOCK_SIZE < transfer->size)
header.length = DATA_BLOCK_SIZE;
else
header.length = transfer->size - transfer->transfered;
out.writeRawData((const char *)(&header), sizeof(TransferHeader));
char * buffer = new char[header.length];
QFile file(transfer->fileDir + QDir::separator() + transfer->fileName);
file.open(QIODevice::ReadOnly);
QDataStream in(&file);
if (in.skipRawData(transfer->transfered) == -1) {
qDebug() << "filename" << file.fileName();
qDebug() << "skip error";
qDebug() << "file size" << transfer->size;
qDebug() << "transfered bytes" << transfer->transfered;
}
if (in.readRawData(buffer, header.length) == -1) {
qDebug() << "Read error";
qDebug() << "buffer size" << header.length;
qDebug() << "file size" << transfer->size;
qDebug() << "transfered bytes" << transfer->transfered;
}
out.writeRawData((const char *)(buffer), header.length);
transfer->transfered += header.length;
delete[] buffer;
if (transfer->transfered >= transfer->size)
transfer->status = TS_CLOSING;
if (transfer->transfered > transfer->size) {
qDebug() << "overread?";
qDebug() << "file size" << transfer->size;
qDebug() << "transfered bytes" << transfer->transfered;
}
emit uploadUpdated(transfer);
}
break;
case TS_CLOSING:
header.type = HT_CLOSE;
header.id = transfer->id;
header.length = 0;
out.writeRawData((const char *)(&header), sizeof(TransferHeader));
transfer->status = TS_FINISHED;
m_Uploads.dequeue();
emit uploadUpdated(transfer);
break;
default:
break;
} }
if (!block.isEmpty())
m_Socket->write(block);
} }
void CWorker::readHeader(QDataStream & in) { void CWorker::readHeader(QDataStream & in) {
@ -292,7 +279,7 @@ namespace qftrans {
target = m_DownloadDict[m_LastHeader.id]; target = m_DownloadDict[m_LastHeader.id];
} }
//id not in dict //id not in dict
else if (m_LastHeader.type == HT_ID) { else if (m_LastHeader.type == HT_ANNOUNCE) {
//new file announced //new file announced
qDebug() << "download announced"; qDebug() << "download announced";
TransferData * newFile = new TransferData; TransferData * newFile = new TransferData;
@ -302,31 +289,27 @@ namespace qftrans {
newFile->localFile = false; newFile->localFile = false;
newFile->status = TS_WAITING; newFile->status = TS_WAITING;
newFile->fileDir = m_DestinationDir; newFile->fileDir = m_DestinationDir;
target = newFile;
} }
//check valid target and data available //check valid target and data available
if (target && m_LastHeader.length) { if (target && m_LastHeader.length) {
switch (m_LastHeader.type) { switch (m_LastHeader.type) {
case HT_ID: case HT_ANNOUNCE:
target->status = TS_WAITING;
// QFile::remove(target->fileDir + QDir::separator() + target->fileName);
emit downloadUpdated(target);
break;
case HT_SIZE:
qDebug() << "reading size.."; qDebug() << "reading size..";
in >> target->size; in >> target->size;
qDebug() << "got size:" << target->size; qDebug() << "got size:" << target->size;
break;
case HT_NAME:
qDebug() << "reading filename.."; qDebug() << "reading filename..";
in >> target->fileName; in >> target->fileName;
qDebug() << "got filename:" << target->fileName; qDebug() << "got filename:" << target->fileName;
emit downloadUpdated(target);
qDebug() << "trying to send HT_ACK"; qDebug() << "trying to send HT_ACK";
//TODO better do this async //TODO better do this async
sendAcknowledge(m_LastHeader.id); sendAcknowledge(m_LastHeader.id);
break; break;
case HT_DATA: { case HT_DATA:
{
// qDebug() << "got data" << target->transfered << '/' << target->size; // qDebug() << "got data" << target->transfered << '/' << target->size;
QFile file(target->fileDir + QDir::separator() + target->fileName); QFile file(target->fileDir + QDir::separator() + target->fileName);
if (target->status == TS_WAITING) { if (target->status == TS_WAITING) {

View File

@ -23,6 +23,7 @@
#include <QObject> #include <QObject>
#include <QTcpSocket> #include <QTcpSocket>
#include <QDataStream> #include <QDataStream>
#include <QQueue>
#include "common.h" #include "common.h"
namespace qftrans { namespace qftrans {
@ -45,7 +46,7 @@ namespace qftrans {
public slots: public slots:
void addUpload(TransferData * transfer); void addUpload(TransferData * transfer);
void removeUpload(TransferData * transfer); void removeUpload(TransferData * transfer);
void startUpload(TransferData * transfer); // void startUpload(TransferData * transfer);
void removeDownload(TransferData * data); void removeDownload(TransferData * data);
@ -66,7 +67,7 @@ namespace qftrans {
QString m_DestinationDir; QString m_DestinationDir;
quint32 m_IdCounter; quint32 m_IdCounter;
QTransferIdHash m_UploadDict; QQueue<TransferData *> m_Uploads;
QTransferDataHash m_DownloadDict; QTransferDataHash m_DownloadDict;

View File

@ -45,12 +45,18 @@ namespace qftrans {
QString status; QString status;
switch (data->status) { switch (data->status) {
case TS_WAITING: case TS_SUSPENDED:
status = tr("waiting"); status = tr("suspended");
break;
case TS_PENDING:
status = tr("pending");
break; break;
case TS_ANNOUNCING: case TS_ANNOUNCING:
status = tr("announcing to peer"); status = tr("announcing to peer");
break; break;
case TS_WAITING:
status = tr("waiting");
break;
case TS_TRANSFERING: case TS_TRANSFERING:
status = data->localFile ? tr("Sending") : tr("Recieving"); status = data->localFile ? tr("Sending") : tr("Recieving");
break; break;