1 #include "instance.h" 2 3 #include <QEventLoop> 4 #include <QTimer> 5 #include <QJsonDocument> 6 #include <QJsonObject> 7 #include <QJsonArray> 8 #include <QApplication> 9 10 namespace IPC { 11 static const auto STATUS_LINE_RESPONSE_OK = "HTTP/1.1 257 R U OK\r\n\r\n"; 12 static const auto STATUS_LINE_REQUEST = "POST / HTTP/1.1\r\n\r\n"; 13 Instance(int prt,int timeo,QObject * parent)14 Instance::Instance(int prt, int timeo, QObject *parent) : QObject(parent), timeout_ms(timeo), port(prt) { 15 connect(&server, &QTcpServer::newConnection, this, &Instance::on_server_connection); 16 } 17 anotherPid() const18 int Instance::anotherPid() const { 19 return send(QVariantMap()); 20 } 21 send(const QVariantMap & data) const22 int Instance::send(const QVariantMap &data) const { 23 QTimer timer; 24 timer.setSingleShot(true); 25 timer.setInterval(timeout_ms); 26 27 QTcpSocket socket; 28 29 int pid = -1; 30 #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) 31 auto conn_error = connect(&socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::errorOccurred), [&](QAbstractSocket::SocketError code) { 32 #else 33 auto conn_error = connect(&socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), [&](QAbstractSocket::SocketError code) { 34 #endif 35 Q_UNUSED(code); 36 pid = -1; 37 }); 38 39 socket.connectToHost(QHostAddress::LocalHost, port); 40 if (socket.waitForConnected(timeout_ms)) { 41 if (socket.state() == QTcpSocket::ConnectedState) { 42 socket.write(STATUS_LINE_REQUEST); 43 socket.write("\r\n"); 44 socket.write(QJsonDocument::fromVariant(data).toJson(QJsonDocument::Compact)); 45 socket.waitForBytesWritten(timeout_ms); 46 if (socket.waitForReadyRead(timeout_ms)) { 47 auto recvd_data = QString::fromUtf8(socket.readAll()); 48 if (recvd_data.startsWith(STATUS_LINE_RESPONSE_OK)) { 49 auto body = QJsonDocument::fromJson(recvd_data.split("\r\n").last().toUtf8()); 50 if (body.isObject()) { 51 pid = body.object()["pid"].toInt(); 52 } 53 } 54 } 55 } 56 } 57 socket.close(); 58 disconnect(conn_error); 59 60 return pid; 61 } 62 63 bool Instance::start() { 64 if (!server.listen(QHostAddress::LocalHost, port)) { 65 qWarning() << "error starting tcp server instance" << server.errorString(); 66 return false; 67 } 68 qDebug() << "first instance started, listen tcp port" << port << "pid" << qApp->applicationPid(); 69 return true; 70 } 71 72 bool Instance::load_files_send(const QStringList &list) { 73 QVariantMap map; 74 map["load_files"] = list; 75 return send(map) > 0; 76 } 77 78 QByteArray Instance::process_received(const QByteArray &request) { 79 QByteArray response(STATUS_LINE_RESPONSE_OK); 80 response.append("\r\n"); 81 82 auto body = QString::fromStdString(request.toStdString()).split("\r\n").last(); 83 auto json_body = QJsonDocument::fromJson(body.toUtf8()); 84 if (json_body.isObject()) { 85 if (json_body.object()["load_files"].isArray()) { 86 QStringList lst; 87 for (auto i : json_body.object()["load_files"].toArray()) { 88 lst.append(i.toString()); 89 } 90 emit load_files_received(lst); 91 } 92 } 93 94 QVariantMap json_body_response; 95 json_body_response["pid"] = qApp->applicationPid(); 96 response.append(QJsonDocument::fromVariant(json_body_response).toJson(QJsonDocument::Compact)); 97 98 return response; 99 } 100 101 QUrl Instance::url() const { 102 auto i = QString("http://%1:%2").arg(QHostAddress(QHostAddress::LocalHost).toString()).arg(port); 103 return QUrl(i); 104 } 105 106 void Instance::on_server_connection() { 107 QTcpSocket *socket = server.nextPendingConnection(); 108 QTimer timer; 109 QEventLoop loop; 110 111 timer.setSingleShot(true); 112 timer.setInterval(timeout_ms); 113 timer.start(); 114 auto conn_read = connect(socket, &QTcpSocket::readyRead, [&]() { 115 socket->write(process_received(socket->readAll())); 116 socket->waitForBytesWritten(timeout_ms); 117 socket->flush(); 118 socket->disconnectFromHost(); 119 loop.quit(); 120 }); 121 auto conn_timer = connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); 122 123 loop.exec(); 124 125 timer.stop(); 126 disconnect(conn_read); 127 disconnect(conn_timer); 128 129 socket->close(); 130 socket->deleteLater(); 131 } 132 } 133