KDEGames

kmessageio.cpp
1 /*
2  This file is part of the KDE games library
3  SPDX-FileCopyrightText: 2001 Burkhard Lehner <[email protected]>
4 
5  SPDX-License-Identifier: LGPL-2.0-only
6 */
7 
8 /*
9  KMessageIO class and subclasses KMessageSocket and KMessageDirect
10 */
11 
12 #include "kmessageio.h"
13 
14 // own
15 #include <kdegamesprivate_kgame_logging.h>
16 // Qt
17 #include <KProcess>
18 #include <QDataStream>
19 #include <QTcpSocket>
20 
21 // ----------------------- KMessageIO -------------------------
22 
24  : QObject(parent)
25  , m_id(0)
26 {
27 }
28 
30 {
31 }
32 
33 void KMessageIO::setId(quint32 id)
34 {
35  m_id = id;
36 }
37 
38 quint32 KMessageIO::id()
39 {
40  return m_id;
41 }
42 
44 {
45  qCCritical(KDEGAMESPRIVATE_KGAME_LOG) << "Calling PURE virtual isNetwork...BAD";
46  return false;
47 }
48 
50 {
51  qCCritical(KDEGAMESPRIVATE_KGAME_LOG) << "Calling PURE virtual isConnected...BAD";
52  return false;
53 }
54 
55 quint16 KMessageIO::peerPort() const
56 {
57  return 0;
58 }
59 
61 {
62  return QStringLiteral("localhost");
63 }
64 
65 // ----------------------KMessageSocket -----------------------
66 
67 KMessageSocket::KMessageSocket(const QString &host, quint16 port, QObject *parent)
68  : KMessageIO(parent)
69 {
70  mSocket = new QTcpSocket();
71  mSocket->connectToHost(host, port);
72  initSocket();
73 }
74 
76  : KMessageIO(parent)
77 {
78  mSocket = new QTcpSocket();
79  mSocket->connectToHost(host.toString(), port);
80  initSocket();
81 }
82 
84  : KMessageIO(parent)
85 {
86  mSocket = socket;
87  initSocket();
88 }
89 
91  : KMessageIO(parent)
92 {
93  mSocket = new QTcpSocket();
94  mSocket->setSocketDescriptor(socketFD);
95  initSocket();
96 }
97 
99 {
100  delete mSocket;
101 }
102 
104 {
105  return mSocket->state() == QAbstractSocket::ConnectedState;
106 }
107 
109 {
110  QDataStream str(mSocket);
111  str << quint8('M'); // magic number for begin of message
112  str.writeBytes(msg.data(), msg.size()); // writes the length (as quint32) and the data
113 }
114 
115 void KMessageSocket::processNewData()
116 {
117  if (isRecursive)
118  return;
119  isRecursive = true;
120 
121  QDataStream str(mSocket);
122  while (mSocket->bytesAvailable() > 0) {
123  if (mAwaitingHeader) {
124  // Header = magic number + packet length = 5 bytes
125  if (mSocket->bytesAvailable() < 5) {
126  isRecursive = false;
127  return;
128  }
129 
130  // Read the magic number first. If something unexpected is found,
131  // start over again, ignoring the data that was read up to then.
132 
133  quint8 v;
134  str >> v;
135  if (v != 'M') {
136  qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": Received unexpected data, magic number wrong!";
137  continue;
138  }
139 
140  str >> mNextBlockLength;
141  mAwaitingHeader = false;
142  } else {
143  // Data not completely read => wait for more
144  if (mSocket->bytesAvailable() < (qint64)mNextBlockLength) {
145  isRecursive = false;
146  return;
147  }
148 
149  QByteArray msg(mNextBlockLength, 0);
150  str.readRawData(msg.data(), mNextBlockLength);
151 
152  // send the received message
153  Q_EMIT received(msg);
154 
155  // Waiting for the header of the next message
156  mAwaitingHeader = true;
157  }
158  }
159 
160  isRecursive = false;
161 }
162 
163 void KMessageSocket::initSocket()
164 {
167  connect(mSocket, &QTcpSocket::readyRead, this, &KMessageSocket::processNewData);
168  mAwaitingHeader = true;
169  mNextBlockLength = 0;
170  isRecursive = false;
171 }
172 
174 {
175  return mSocket->peerPort();
176 }
177 
179 {
180  return mSocket->peerName();
181 }
182 
183 // ----------------------KMessageDirect -----------------------
184 
186  : KMessageIO(parent)
187  , mPartner(nullptr)
188 {
189  // 0 as first parameter leaves the object unconnected
190  if (!partner)
191  return;
192 
193  // Check if the other object is already connected
194  if (partner && partner->mPartner) {
195  qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": Object is already connected!";
196  return;
197  }
198 
199  // Connect from us to that object
200  mPartner = partner;
201 
202  // Connect the other object to us
203  partner->mPartner = this;
204 }
205 
207 {
208  if (mPartner) {
209  mPartner->mPartner = nullptr;
210  Q_EMIT mPartner->connectionBroken();
211  }
212 }
213 
215 {
216  return mPartner != nullptr;
217 }
218 
220 {
221  if (mPartner)
222  Q_EMIT mPartner->received(msg);
223  else
224  qCCritical(KDEGAMESPRIVATE_KGAME_LOG) << ": Not yet connected!";
225 }
226 
227 // ----------------------- KMessageProcess ---------------------------
228 
229 KMessageProcess::~KMessageProcess()
230 {
231  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess::Delete process";
232  if (mProcess) {
233  mProcess->kill();
234  mProcess->deleteLater();
235  mProcess = nullptr;
236  // Maybe todo: delete mSendBuffer
237  }
238 }
239 KMessageProcess::KMessageProcess(QObject *parent, const QString &file)
240  : KMessageIO(parent)
241 {
242  // Start process
243  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess::Start process";
244  mProcessName = file;
245  mProcess = new KProcess;
246  // Need both stdout and stderr as separate channels in the communication
248  int id = 0;
249  *mProcess << mProcessName << QStringLiteral("%1").arg(id);
250  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess::Init:Id=" << id;
251  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessgeProcess::Init:Processname:" << mProcessName;
252  connect(mProcess, &KProcess::readyReadStandardOutput, this, &KMessageProcess::slotReceivedStdout);
253  connect(mProcess, &KProcess::readyReadStandardError, this, &KMessageProcess::slotReceivedStderr);
254  connect(mProcess, static_cast<void (KProcess::*)(int, QProcess::ExitStatus)>(&KProcess::finished), this, &KMessageProcess::slotProcessExited);
255  mProcess->start();
256  mSendBuffer = nullptr;
257  mReceiveCount = 0;
258  mReceiveBuffer.resize(1024);
259 }
260 bool KMessageProcess::isConnected() const
261 {
262  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess::Is connected";
263  if (!mProcess)
264  return false;
265  return (mProcess->state() == QProcess::Running);
266 }
267 
268 // Send to process
269 void KMessageProcess::send(const QByteArray &msg)
270 {
271  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess:: SEND(" << msg.size() << ") to process";
272  unsigned int size = msg.size() + 2 * sizeof(long);
273 
274  if (mProcess == nullptr) {
275  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess:: cannot write to stdin, no process available";
276  return;
277  }
278 
279  char *tmpbuffer = new char[size];
280  long *p1 = (long *)tmpbuffer;
281  long *p2 = p1 + 1;
282  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "p1=" << p1 << "p2=" << p2;
283  memcpy(tmpbuffer + 2 * sizeof(long), msg.data(), msg.size());
284  *p1 = 0x4242aeae;
285  *p2 = size;
286 
287  // no need to add it to a queue -> qiodevice is buffered
288  mProcess->write(tmpbuffer, size);
289  delete[] tmpbuffer;
290 }
291 
292 void KMessageProcess::slotReceivedStderr()
293 {
294  QByteArray ba;
295  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@ KMessageProcess::slotReceivedStderr";
296 
297  mProcess->setReadChannel(QProcess::StandardError);
298  while (mProcess->canReadLine()) {
299  ba = mProcess->readLine();
300  if (ba.isEmpty())
301  return;
302  ba.chop(1); // remove QLatin1Char( '\n' )
303 
304  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "KProcess (" << ba.size() << "):" << ba.constData();
305  Q_EMIT signalReceivedStderr(QLatin1String(ba));
306  ba.clear();
307  };
308 }
309 
310 void KMessageProcess::slotReceivedStdout()
311 {
312  mProcess->setReadChannel(QProcess::StandardOutput);
313  QByteArray ba = mProcess->readAll();
314  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "$$$$$$ "
315  << ": Received" << ba.size() << "bytes over inter process communication";
316 
317  // Resize receive buffer
318  while (mReceiveCount + ba.size() >= mReceiveBuffer.size())
319  mReceiveBuffer.resize(mReceiveBuffer.size() + 1024);
320  // was 08/2007: mReceiveBuffer += ba;
321  std::copy(ba.begin(), ba.begin() + ba.size(), mReceiveBuffer.begin() + mReceiveCount);
322  mReceiveCount += ba.size();
323 
324  // Possible message
325  while (mReceiveCount > int(2 * sizeof(long))) {
326  long *p1 = (long *)mReceiveBuffer.data();
327  long *p2 = p1 + 1;
328  int len;
329  if (*p1 != 0x4242aeae) {
330  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Cookie error...transmission failure...serious problem...";
331  }
332  len = (int)(*p2);
333  if (len < int(2 * sizeof(long))) {
334  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Message size error";
335  break;
336  }
337  if (len <= mReceiveCount) {
338  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Got message with len" << len;
339 
340  QByteArray msg;
341  msg.resize(len);
342  // msg.setRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
343 
344  std::copy(mReceiveBuffer.begin() + 2 * sizeof(long), mReceiveBuffer.begin() + len, msg.begin());
345  // msg.duplicate(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
346  Q_EMIT received(msg);
347  // msg.resetRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
348  // Shift buffer
349  if (len < mReceiveCount) {
350  memmove(mReceiveBuffer.data(), mReceiveBuffer.data() + len, mReceiveCount - len);
351  }
352  mReceiveCount -= len;
353  } else
354  break;
355  }
356 }
357 
358 void KMessageProcess::slotProcessExited(int exitCode, QProcess::ExitStatus)
359 {
360  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "Process exited (slot) with code" << exitCode;
361  Q_EMIT connectionBroken();
362  delete mProcess;
363  mProcess = nullptr;
364 }
365 
366 #include "moc_kmessageio.cpp"
virtual void connectToHost(const QString &hostName, quint16 port, QIODevice::OpenMode openMode, QAbstractSocket::NetworkLayerProtocol protocol)
~KMessageSocket() override
Destructor, closes the socket.
Definition: kmessageio.cpp:98
void connectionBroken()
This signal is emitted when the connection is closed.
Q_EMITQ_EMIT
virtual bool isConnected() const
This method returns the status of the object, whether it is already (or still) connected to another K...
Definition: kmessageio.cpp:49
void send(const QByteArray &msg) override
Overwritten slot method from KMessageIO.
Definition: kmessageio.cpp:108
void clear()
void finished(int exitCode)
~KMessageDirect() override
Destructor, closes the connection.
Definition: kmessageio.cpp:206
void setOutputChannelMode(OutputChannelMode mode)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
KMessageDirect(KMessageDirect *partner=nullptr, QObject *parent=nullptr)
Creates an object and connects it to the object given in the first parameter.
Definition: kmessageio.cpp:185
void readyReadStandardOutput()
QByteArray::iterator begin()
virtual QString peerName() const
Definition: kmessageio.cpp:60
void chop(int n)
QString peerName() const const
void readyRead()
QString peerName() const override
Definition: kmessageio.cpp:178
void errorOccurred(QAbstractSocket::SocketError socketError)
void readyReadStandardError()
quint16 peerPort() const const
void received(const QByteArray &msg)
This signal is emitted when /e send() on the connected KMessageIO object is called.
void setId(quint32 id)
Sets the ID number of this object.
Definition: kmessageio.cpp:33
bool isConnected() const override
Returns true if the socket is in state /e connected.
Definition: kmessageio.cpp:103
ScriptableExtension * host() const
bool isEmpty() const const
void resize(int size)
const char * constData() const const
void send(const QByteArray &msg) override
Overwritten slot method from KMessageIO.
Definition: kmessageio.cpp:219
virtual qint64 bytesAvailable() const const override
virtual bool setSocketDescriptor(qintptr socketDescriptor, QAbstractSocket::SocketState socketState, QIODevice::OpenMode openMode)
quint32 id()
Queries the ID of this object.
Definition: kmessageio.cpp:38
KMessageSocket(const QString &host, quint16 port, QObject *parent=nullptr)
Connects to a server socket on /e host with /e port.
Definition: kmessageio.cpp:67
virtual bool isNetwork() const
Definition: kmessageio.cpp:43
bool isConnected() const override
Returns true, if the object is connected to another instance.
Definition: kmessageio.cpp:214
int size() const const
quint16 peerPort() const override
Definition: kmessageio.cpp:173
QDataStream & writeBytes(const char *s, uint len)
KMessageIO(QObject *parent=nullptr)
The usual QObject constructor, does nothing else.
Definition: kmessageio.cpp:23
virtual quint16 peerPort() const
Definition: kmessageio.cpp:55
~KMessageIO() override
The usual destructor, does nothing special.
Definition: kmessageio.cpp:29
char * data()
QAbstractSocket::SocketState state() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Fri Dec 1 2023 04:08:56 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.