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

KDE's Doxygen guidelines are available online.