Betrachten Sie nun den Server und den Client.
Fragen zur allgemeinen Organisation der Client-Server-Interaktion finden Sie in diesem Artikel: „ An example of using QLocalServer and QLocalSocket “. Hier werde ich nur die Punkte ansprechen, die in direktem Zusammenhang mit der Übermittlung von Informationen zur Aufgabe und dem Abschluss der Aufgabe stehen.
Server
// trackerserver.cpp TrackerServer::TrackerServer(QWidget* parent) : QWidget(parent) { // Задаём имя сервера nameServer = "TrackerServer"; // Устанавливаем размер следующего блока равным нулю nextBlockSize = 0; ... } // Метод создания и запуска сервера void TrackerServer::createServer() { localServer = new QLocalServer(this); // Если сервер не запустить, выдать сообщение и завершить программу if(!localServer->listen(nameServer)) { QMessageBox::critical(0, tr("Ошибка сервера"), tr("Невозможно запустить сервер ") + nameServer + ": " + localServer->errorString()); localServer->close(); return; } } ... // Метод создания подключений void TrackerServer::createConnections() { // Подключение сигнала сервера о новом подключении к обработчику нового подключения connect(localServer, &QLocalServer::newConnection, this, &TrackerServer::slotNewConnection); ... }
Betrachten Sie nun die Serversteckplätze.
// trackerserver.cpp // Слот обработки нового соединения void TrackerServer::slotNewConnection() { // Получаем сокет, подключённый к серверу QLocalSocket* socket = localServer->nextPendingConnection(); // Соединяем сигнал отключения сокета с обработчиком удаления сокета connect(socket, &QLocalSocket::disconnected, socket, &QLocalSocket::deleteLater); // Соединяем сигнал сокета о готовности передачи данных со слотом закрытия задачи connect(socket, &QLocalSocket::readyRead, this, &TrackerServer::slotCloseTask); } // Слот получения информации от клиента и закрытия задачи void TrackerServer::slotCloseTask() { QLocalSocket* socket = (QLocalSocket*)sender(); QDataStream in(socket); in.setVersion(QDataStream::Qt_5_3); for(;;) { if(!nextBlockSize) { if(socket->bytesAvailable() < (int)sizeof(quint16)) break; in >> nextBlockSize; } in >> clientTaskNumber >> clientRevision; nextBlockSize = 0; } if(!database->isClosed(clientTaskNumber) && clientTaskNumber && clientRevision) // Если задача не закрыта и если номер задачи и ревизия не пустые, закрываем задачу { database->closeTask(clientTaskNumber, QString("%1").arg(clientRevision)); // Обновляем SQL-таблицу задач taskSqlTableModel->select(); // Если все задачи для текущего проекта закрыты if(database->isClosedAllTasks(projectCurrentId)) // сделать доступной кнопку "Архивировать" archiveProjectButton->setEnabled(true); else // иначе блокировать кнопку "Архивировать" archiveProjectButton->setDisabled(true); } }
Kunde
// main.cpp #include "trackerclient.h" #include <QCoreApplication> #include <QFile> #include <QTextStream> #include <QDebug> #include <QRegExp> int main(int argc, char* argv[]) { QCoreApplication app(argc, argv); // Считываем из аргумента 1 командной строки путь к файлу log.txt QString pathToFile = argv[1]; QString result; // Если файл открывается, считываем данные из файла в строку QFile file(pathToFile); if(file.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream stream(&file); result = stream.readAll(); file.close(); } else qDebug() << "Unable open file " + pathToFile; int taskNumber = 0; int revisionNumber = 0; // С помощью регулярного выражения извлекаем из строки номер задачи и номер ревизии QString expr = "(.*)(\\#\\{)(\\d+)(\\})(.*)(r\\{)(\\d+)(\\})"; QRegExp taskRevision(expr); int position = taskRevision.indexIn(result); if(position > -1) { taskNumber = taskRevision.cap(3).toInt(); // это номер задачи revisionNumber = taskRevision.cap(7).toInt(); // это номер ревизии } // Инициализируем клиента с соответствующими параметрами TrackerClient client("TrackerServer", taskNumber, revisionNumber); app.exit(0); }
Analysieren wir nun die Frage, woher die log.txt kommt.
Wie ich im vorherigen Teil geschrieben habe, können Sie zur Vereinfachung des Trackers die Schaltfläche zur Projekterstellung verwenden, um nur ein Projekt ohne VCS-Unterstützung zu erstellen. Das heißt, ein solches virtuelles Projekt, das nur in der Tracker-Datenbank existiert und nirgendwo sonst.
Projekte mit SLE-Unterstützung werden mit einer bat-Datei erstellt. Ich habe es etwas früher aus der Notwendigkeit heraus geschrieben, SVN-Repositories zu erstellen und zu verwenden, damit sie automatisch archiviert werden, wenn es Commits gibt. Betrachten wir es in dem Teil, der die Erstellung von Projekten mit Unterstützung für harte Währung betrifft, dh Projekte, die bereits im Dateisystem vorhanden sind.
rem Запрашиваем наименование проекта set /p repo_name=[Input name project]: echo Name repository: %repo_name% rem Устанавливаем путь к репозиторию set repo_path=%repo_root%\%repo_name% echo Full path to repository: %repo_path% rem Создаём репозиторий svnadmin create %repo_path% rem Очищаем папку репозитория hooks del /f /q %repo_path%\hooks\ rem Создаём / копируем в папке hooks файлы ini.bat, pre-commit.bat, post-commit.bat echo set commit=n>%repo_path%\hooks\ini.bat copy pre-commit.bat %repo_path%\hooks\ copy post-commit2.bat %repo_path%\hooks\post-commit.bat rem Импортируем файловую структуру по умолчанию и формируем первый коммит svn import %def_tree% file:///%repo_path% -m "initial import"
Die Datei ini.bat enthält das Commit-Flag, das auf „n“ gesetzt ist, wenn es keine Commits gibt, oder auf „y“, wenn es mindestens ein Commit gibt. Basierend auf seinem Status bestimmt das Archivierungsskript, ob das angegebene Repository archiviert werden soll.
rem pre-commit.bat @echo off rem Считываем переменные из командной строки set repo_path=%1 set transaction=%2 rem Записываем сообщение коммита в файл log.txt "G:\soft\svn\bin\svnlook.exe" log -t "%transaction%" "%repo_path%">%repo_path%\hooks\log.txt
Die Commit-Nachricht enthält die Nummer der zu schließenden Aufgabe im Format #{ <Problemnummer> } .
rem post-commit.bat @echo off rem Считываем переменные из командной строки set repo_path=%1 set revision=%2 rem Устанавливаем в файле ini.bat флаг commit в "y" echo set commit=y>%repo_path%\hooks\ini.bat rem Записываем номер ревизии в файл log.txt echo r{%revision%}>>%repo_path%\hooks\log.txt rem %path_to_tracker%\ICTrackerClient.exe %repo_path%\hooks\log.txt
Nach dem Schreiben der Revision in post-commit.bat sieht die log.txt-Datei etwa so aus.
Closing task #{2} end some editing files r{29}
Und der letzte Client wird aufgerufen, der diese Daten aus der Datei liest.
Es bleibt nur noch, diese Daten an den Server zu übertragen.
// trackerclient.h #ifndef TRACKERCLIENT_H #define TRACKERCLIENT_H #include <QObject> #include <QLocalSocket> class TrackerClient : public QObject { Q_OBJECT public: explicit TrackerClient(const QString& serverName, int taskId, int revision, QObject* parent = 0); ~TrackerClient(); private: QLocalSocket* localSocket; int taskId; int revision; private slots: void sendToServer(); }; #endif // TRACKERCLIENT_H
// trackerclient.cpp #include "trackerclient.h" #include <QDataStream> TrackerClient::TrackerClient(const QString &serverName, int taskId, int revision, QObject* parent) : QObject(parent), taskId(taskId), revision(revision) { localSocket = new QLocalSocket(this); localSocket->setServerName(serverName); connect(localSocket, &QLocalSocket::connected, this, &TrackerClient::sendToServer); localSocket->connectToServer(); } TrackerClient::~TrackerClient() { } void TrackerClient::sendToServer() { QByteArray arrayBlock; QDataStream out(&arrayBlock, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_5_3); out << quint16(0) << taskId << revision; out.device()->seek(0); out << quint16(arrayBlock.size() - sizeof(quint16)); localSocket->write(arrayBlock); localSocket->disconnectFromServer(); }
Добрый день, и снова немного ревью.
Вот это каст в стиле Си
Лучше кастовать в стиле С++, мы же на С++ пишем.
Получается несколько многословнее, но честно, в большом проекте хоть есть за что глазу зацепиться. Плюс более контролируемый каст.
Далее, думаю, что вот это
Можно переписать так
setEnabled и setDisabled делают тоже самое.
Вот это
Нужно переписать так
nullptr - это специальный псевдотип данных для нулевого указателя. В случае с 0, там по сути может оказаться какой угодно мусор в зависимости от платформы. В случае с nullptr поведение будет предсказуемым.
Далее вот это объявление
Наверное стоит вам сразу писать как
Это принятый код-стайл в Qt, а также именно на такую запись заточены макросы Q_PROPERTY. Ну и стоит в деструкторе удалять m_localSocket, чтобы не было утчечек памяти. Вообще этот как раз тот случай, где имеет смысл использовать умные указатели, как вы делали в прошлой статье.
Да, я уже со static_cast освоился, такая запись понятнее.
Ага.
Понял.
А что означает буква m перед наименованием? Эти буквы меня всегда путают и с толку сбивают.
member - член класса, некоторое поле. В Qt это обычное обозначение тех объектов, которые объявлены в private или protected секциях.
Просто в Q_PROPERTY это прописывается без m_ но макрос разворачивается на член класса, который содержит этот префикс.
Как правильно объявить умный указатель и инициализировать по отдельности? Я пробовал так:
выдаёт ошибку при компиляции.
Теперь на коннекте ошибку выдаёт:
connect(m_localSocket.get(), &QLocalSocket::connected, this, &TrackerClient::sendToServer);