KDEGames

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

KDE's Doxygen guidelines are available online.