File indexing completed on 2024-12-22 03:52:57
0001 /* 0002 This file is part of the KDE games library 0003 SPDX-FileCopyrightText: 2001 Burkhard Lehner <Burkhard.Lehner@gmx.de> 0004 0005 SPDX-License-Identifier: LGPL-2.0-only 0006 */ 0007 0008 /* 0009 KMessageIO class and subclasses KMessageSocket and KMessageDirect 0010 */ 0011 0012 #include "kmessageio.h" 0013 0014 // own 0015 #include <kdegamesprivate_kgame_logging.h> 0016 // Qt 0017 #include <KProcess> 0018 #include <QDataStream> 0019 #include <QTcpSocket> 0020 0021 // ----------------------- KMessageIO ------------------------- 0022 0023 KMessageIO::KMessageIO(QObject *parent) 0024 : QObject(parent) 0025 , m_id(0) 0026 { 0027 } 0028 0029 KMessageIO::~KMessageIO() 0030 { 0031 } 0032 0033 void KMessageIO::setId(quint32 id) 0034 { 0035 m_id = id; 0036 } 0037 0038 quint32 KMessageIO::id() 0039 { 0040 return m_id; 0041 } 0042 0043 bool KMessageIO::isNetwork() const 0044 { 0045 qCCritical(KDEGAMESPRIVATE_KGAME_LOG) << "Calling PURE virtual isNetwork...BAD"; 0046 return false; 0047 } 0048 0049 bool KMessageIO::isConnected() const 0050 { 0051 qCCritical(KDEGAMESPRIVATE_KGAME_LOG) << "Calling PURE virtual isConnected...BAD"; 0052 return false; 0053 } 0054 0055 quint16 KMessageIO::peerPort() const 0056 { 0057 return 0; 0058 } 0059 0060 QString KMessageIO::peerName() const 0061 { 0062 return QStringLiteral("localhost"); 0063 } 0064 0065 // ----------------------KMessageSocket ----------------------- 0066 0067 KMessageSocket::KMessageSocket(const QString &host, quint16 port, QObject *parent) 0068 : KMessageIO(parent) 0069 { 0070 mSocket = new QTcpSocket(); 0071 mSocket->connectToHost(host, port); 0072 initSocket(); 0073 } 0074 0075 KMessageSocket::KMessageSocket(const QHostAddress &host, quint16 port, QObject *parent) 0076 : KMessageIO(parent) 0077 { 0078 mSocket = new QTcpSocket(); 0079 mSocket->connectToHost(host.toString(), port); 0080 initSocket(); 0081 } 0082 0083 KMessageSocket::KMessageSocket(QTcpSocket *socket, QObject *parent) 0084 : KMessageIO(parent) 0085 { 0086 mSocket = socket; 0087 initSocket(); 0088 } 0089 0090 KMessageSocket::KMessageSocket(int socketFD, QObject *parent) 0091 : KMessageIO(parent) 0092 { 0093 mSocket = new QTcpSocket(); 0094 mSocket->setSocketDescriptor(socketFD); 0095 initSocket(); 0096 } 0097 0098 KMessageSocket::~KMessageSocket() 0099 { 0100 delete mSocket; 0101 } 0102 0103 bool KMessageSocket::isConnected() const 0104 { 0105 return mSocket->state() == QAbstractSocket::ConnectedState; 0106 } 0107 0108 void KMessageSocket::send(const QByteArray &msg) 0109 { 0110 QDataStream str(mSocket); 0111 str << quint8('M'); // magic number for begin of message 0112 str.writeBytes(msg.data(), msg.size()); // writes the length (as quint32) and the data 0113 } 0114 0115 void KMessageSocket::processNewData() 0116 { 0117 if (isRecursive) 0118 return; 0119 isRecursive = true; 0120 0121 QDataStream str(mSocket); 0122 while (mSocket->bytesAvailable() > 0) { 0123 if (mAwaitingHeader) { 0124 // Header = magic number + packet length = 5 bytes 0125 if (mSocket->bytesAvailable() < 5) { 0126 isRecursive = false; 0127 return; 0128 } 0129 0130 // Read the magic number first. If something unexpected is found, 0131 // start over again, ignoring the data that was read up to then. 0132 0133 quint8 v; 0134 str >> v; 0135 if (v != 'M') { 0136 qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": Received unexpected data, magic number wrong!"; 0137 continue; 0138 } 0139 0140 str >> mNextBlockLength; 0141 mAwaitingHeader = false; 0142 } else { 0143 // Data not completely read => wait for more 0144 if (mSocket->bytesAvailable() < (qint64)mNextBlockLength) { 0145 isRecursive = false; 0146 return; 0147 } 0148 0149 QByteArray msg(mNextBlockLength, 0); 0150 str.readRawData(msg.data(), mNextBlockLength); 0151 0152 // send the received message 0153 Q_EMIT received(msg); 0154 0155 // Waiting for the header of the next message 0156 mAwaitingHeader = true; 0157 } 0158 } 0159 0160 isRecursive = false; 0161 } 0162 0163 void KMessageSocket::initSocket() 0164 { 0165 connect(mSocket, &QTcpSocket::errorOccurred, this, &KMessageSocket::connectionBroken); 0166 connect(mSocket, &QTcpSocket::disconnected, this, &KMessageSocket::connectionBroken); 0167 connect(mSocket, &QTcpSocket::readyRead, this, &KMessageSocket::processNewData); 0168 mAwaitingHeader = true; 0169 mNextBlockLength = 0; 0170 isRecursive = false; 0171 } 0172 0173 quint16 KMessageSocket::peerPort() const 0174 { 0175 return mSocket->peerPort(); 0176 } 0177 0178 QString KMessageSocket::peerName() const 0179 { 0180 return mSocket->peerName(); 0181 } 0182 0183 // ----------------------KMessageDirect ----------------------- 0184 0185 KMessageDirect::KMessageDirect(KMessageDirect *partner, QObject *parent) 0186 : KMessageIO(parent) 0187 , mPartner(nullptr) 0188 { 0189 // 0 as first parameter leaves the object unconnected 0190 if (!partner) 0191 return; 0192 0193 // Check if the other object is already connected 0194 if (partner && partner->mPartner) { 0195 qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": Object is already connected!"; 0196 return; 0197 } 0198 0199 // Connect from us to that object 0200 mPartner = partner; 0201 0202 // Connect the other object to us 0203 partner->mPartner = this; 0204 } 0205 0206 KMessageDirect::~KMessageDirect() 0207 { 0208 if (mPartner) { 0209 mPartner->mPartner = nullptr; 0210 Q_EMIT mPartner->connectionBroken(); 0211 } 0212 } 0213 0214 bool KMessageDirect::isConnected() const 0215 { 0216 return mPartner != nullptr; 0217 } 0218 0219 void KMessageDirect::send(const QByteArray &msg) 0220 { 0221 if (mPartner) 0222 Q_EMIT mPartner->received(msg); 0223 else 0224 qCCritical(KDEGAMESPRIVATE_KGAME_LOG) << ": Not yet connected!"; 0225 } 0226 0227 // ----------------------- KMessageProcess --------------------------- 0228 0229 KMessageProcess::~KMessageProcess() 0230 { 0231 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess::Delete process"; 0232 if (mProcess) { 0233 mProcess->kill(); 0234 mProcess->deleteLater(); 0235 mProcess = nullptr; 0236 // Maybe todo: delete mSendBuffer 0237 } 0238 } 0239 KMessageProcess::KMessageProcess(QObject *parent, const QString &file) 0240 : KMessageIO(parent) 0241 { 0242 // Start process 0243 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess::Start process"; 0244 mProcessName = file; 0245 mProcess = new KProcess; 0246 // Need both stdout and stderr as separate channels in the communication 0247 mProcess->setOutputChannelMode(KProcess::SeparateChannels); 0248 int id = 0; 0249 *mProcess << mProcessName << QStringLiteral("%1").arg(id); 0250 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess::Init:Id=" << id; 0251 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessgeProcess::Init:Processname:" << mProcessName; 0252 connect(mProcess, &KProcess::readyReadStandardOutput, this, &KMessageProcess::slotReceivedStdout); 0253 connect(mProcess, &KProcess::readyReadStandardError, this, &KMessageProcess::slotReceivedStderr); 0254 connect(mProcess, static_cast<void (KProcess::*)(int, QProcess::ExitStatus)>(&KProcess::finished), this, &KMessageProcess::slotProcessExited); 0255 mProcess->start(); 0256 mSendBuffer = nullptr; 0257 mReceiveCount = 0; 0258 mReceiveBuffer.resize(1024); 0259 } 0260 bool KMessageProcess::isConnected() const 0261 { 0262 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess::Is connected"; 0263 if (!mProcess) 0264 return false; 0265 return (mProcess->state() == QProcess::Running); 0266 } 0267 0268 // Send to process 0269 void KMessageProcess::send(const QByteArray &msg) 0270 { 0271 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess:: SEND(" << msg.size() << ") to process"; 0272 unsigned int size = msg.size() + 2 * sizeof(long); 0273 0274 if (mProcess == nullptr) { 0275 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess:: cannot write to stdin, no process available"; 0276 return; 0277 } 0278 0279 char *tmpbuffer = new char[size]; 0280 long *p1 = (long *)tmpbuffer; 0281 long *p2 = p1 + 1; 0282 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "p1=" << p1 << "p2=" << p2; 0283 memcpy(tmpbuffer + 2 * sizeof(long), msg.data(), msg.size()); 0284 *p1 = 0x4242aeae; 0285 *p2 = size; 0286 0287 // no need to add it to a queue -> qiodevice is buffered 0288 mProcess->write(tmpbuffer, size); 0289 delete[] tmpbuffer; 0290 } 0291 0292 void KMessageProcess::slotReceivedStderr() 0293 { 0294 QByteArray ba; 0295 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@ KMessageProcess::slotReceivedStderr"; 0296 0297 mProcess->setReadChannel(QProcess::StandardError); 0298 while (mProcess->canReadLine()) { 0299 ba = mProcess->readLine(); 0300 if (ba.isEmpty()) 0301 return; 0302 ba.chop(1); // remove QLatin1Char( '\n' ) 0303 0304 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "KProcess (" << ba.size() << "):" << ba.constData(); 0305 Q_EMIT signalReceivedStderr(QLatin1String(ba)); 0306 ba.clear(); 0307 }; 0308 } 0309 0310 void KMessageProcess::slotReceivedStdout() 0311 { 0312 mProcess->setReadChannel(QProcess::StandardOutput); 0313 QByteArray ba = mProcess->readAll(); 0314 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "$$$$$$ " 0315 << ": Received" << ba.size() << "bytes over inter process communication"; 0316 0317 // Resize receive buffer 0318 while (mReceiveCount + ba.size() >= mReceiveBuffer.size()) 0319 mReceiveBuffer.resize(mReceiveBuffer.size() + 1024); 0320 // was 08/2007: mReceiveBuffer += ba; 0321 std::copy(ba.begin(), ba.begin() + ba.size(), mReceiveBuffer.begin() + mReceiveCount); 0322 mReceiveCount += ba.size(); 0323 0324 // Possible message 0325 while (mReceiveCount > int(2 * sizeof(long))) { 0326 long *p1 = (long *)mReceiveBuffer.data(); 0327 long *p2 = p1 + 1; 0328 int len; 0329 if (*p1 != 0x4242aeae) { 0330 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Cookie error...transmission failure...serious problem..."; 0331 } 0332 len = (int)(*p2); 0333 if (len < int(2 * sizeof(long))) { 0334 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Message size error"; 0335 break; 0336 } 0337 if (len <= mReceiveCount) { 0338 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Got message with len" << len; 0339 0340 QByteArray msg; 0341 msg.resize(len); 0342 // msg.setRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long)); 0343 0344 std::copy(mReceiveBuffer.begin() + 2 * sizeof(long), mReceiveBuffer.begin() + len, msg.begin()); 0345 // msg.duplicate(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long)); 0346 Q_EMIT received(msg); 0347 // msg.resetRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long)); 0348 // Shift buffer 0349 if (len < mReceiveCount) { 0350 memmove(mReceiveBuffer.data(), mReceiveBuffer.data() + len, mReceiveCount - len); 0351 } 0352 mReceiveCount -= len; 0353 } else 0354 break; 0355 } 0356 } 0357 0358 void KMessageProcess::slotProcessExited(int exitCode, QProcess::ExitStatus) 0359 { 0360 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "Process exited (slot) with code" << exitCode; 0361 Q_EMIT connectionBroken(); 0362 delete mProcess; 0363 mProcess = nullptr; 0364 } 0365 0366 #include "moc_kmessageio.cpp"