KDEGames

kmessageio.cpp
1/*
2 This file is part of the KDE games library
3 SPDX-FileCopyrightText: 2001 Burkhard Lehner <Burkhard.Lehner@gmx.de>
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
32
33void KMessageIO::setId(quint32 id)
34{
35 m_id = id;
36}
37
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
55quint16 KMessageIO::peerPort() const
56{
57 return 0;
58}
59
61{
62 return QStringLiteral("localhost");
63}
64
65// ----------------------KMessageSocket -----------------------
66
67KMessageSocket::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
75KMessageSocket::KMessageSocket(const QHostAddress &host, quint16 port, QObject *parent)
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();
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
115void 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
163void 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
229KMessageProcess::~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}
239KMessageProcess::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}
260bool 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
269void 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
292void KMessageProcess::slotReceivedStderr()
293{
295 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@ KMessageProcess::slotReceivedStderr";
296
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
310void KMessageProcess::slotReceivedStdout()
311{
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
358void KMessageProcess::slotProcessExited(int exitCode, QProcess::ExitStatus)
359{
360 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "Process exited (slot) with code" << exitCode;
362 delete mProcess;
363 mProcess = nullptr;
364}
365
366#include "moc_kmessageio.cpp"
This class implements the message communication using function calls directly.
Definition kmessageio.h:279
~KMessageDirect() override
Destructor, closes the connection.
void send(const QByteArray &msg) override
Overwritten slot method from KMessageIO.
KMessageDirect(KMessageDirect *partner=nullptr, QObject *parent=nullptr)
Creates an object and connects it to the object given in the first parameter.
bool isConnected() const override
Returns true, if the object is connected to another instance.
This abstract base class represents one end of a message connections between two clients.
Definition kmessageio.h:44
virtual bool isNetwork() const
quint32 id()
Queries the ID of this object.
void connectionBroken()
This signal is emitted when the connection is closed.
virtual bool isConnected() const
This method returns the status of the object, whether it is already (or still) connected to another K...
~KMessageIO() override
The usual destructor, does nothing special.
virtual quint16 peerPort() const
void received(const QByteArray &msg)
This signal is emitted when /e send() on the connected KMessageIO object is called.
KMessageIO(QObject *parent=nullptr)
The usual QObject constructor, does nothing else.
void setId(quint32 id)
Sets the ID number of this object.
virtual QString peerName() const
~KMessageSocket() override
Destructor, closes the socket.
quint16 peerPort() const override
KMessageSocket(const QString &host, quint16 port, QObject *parent=nullptr)
Connects to a server socket on /e host with /e port.
QString peerName() const override
void send(const QByteArray &msg) override
Overwritten slot method from KMessageIO.
bool isConnected() const override
Returns true if the socket is in state /e connected.
void setOutputChannelMode(OutputChannelMode mode)
virtual qint64 bytesAvailable() const const override
void connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode)
void errorOccurred(QAbstractSocket::SocketError socketError)
QString peerName() const const
quint16 peerPort() const const
virtual bool setSocketDescriptor(qintptr socketDescriptor, SocketState socketState, OpenMode openMode)
SocketState state() const const
iterator begin()
char * data()
void resize(qsizetype newSize, char c)
qsizetype size() const const
QString toString() const const
virtual bool canReadLine() const const
QByteArray readAll()
QByteArray readLine(qint64 maxSize)
void readyRead()
qint64 write(const QByteArray &data)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
T qobject_cast(QObject *object)
void finished(int exitCode, QProcess::ExitStatus exitStatus)
void kill()
void readyReadStandardError()
void readyReadStandardOutput()
void setReadChannel(ProcessChannel channel)
QProcess::ProcessState state() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:16:50 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.