KDEGames

kmessageclient.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 #include "kmessageclient.h"
21 
22 #include <stdio.h>
23 
24 #include <QBuffer>
25 #include <QTimer>
26 #include <QList>
27 #include <QDataStream>
28 
29 #include "kmessageio.h"
30 #include "kmessageserver.h"
31 
32 class KMessageClientPrivate
33 {
34 public:
35  KMessageClientPrivate ()
36  : adminID (0), connection (nullptr)
37  {}
38 
39  ~KMessageClientPrivate ()
40  {
41  delete connection;
42  }
43 
44  quint32 adminID;
45  QList <quint32> clientList;
46  KMessageIO *connection;
47 
48  bool isLocked;
49  QList <QByteArray> delayedMessages;
50 };
51 
53  : QObject (parent),
54  d( new KMessageClientPrivate )
55 {
56  d->isLocked = false;
57 }
58 
60 {
61  d->delayedMessages.clear();
62  delete d;
63 }
64 
65 // -- setServer stuff
66 
67 void KMessageClient::setServer (const QString &host, quint16 port)
68 {
69  setServer (new KMessageSocket (host, port));
70 }
71 
73 {
74  KMessageDirect *serverIO = new KMessageDirect ();
75  setServer (new KMessageDirect (serverIO));
76  server->addClient (serverIO);
77 }
78 
80 {
81  if (d->connection)
82  {
83  delete d->connection;
84  qCDebug(GAMES_PRIVATE_KGAME) << ": We are changing the server!";
85  }
86 
87  d->connection = connection;
88  if (connection )
89  {
92  }
93 }
94 
95 // -- id stuff
96 
97 quint32 KMessageClient::id () const
98 {
99  return (d->connection) ? d->connection->id () : 0;
100 }
101 
103 {
104  return id() != 0 && id() == adminId();
105 }
106 
107 quint32 KMessageClient::adminId () const
108 {
109  return d->adminID;
110 }
111 
113 {
114  return d->clientList;
115 }
116 
118 {
119  return d->connection && d->connection->isConnected();
120 }
121 
123 {
124  return isConnected() ? d->connection->isNetwork() : false;
125 }
126 
127 quint16 KMessageClient::peerPort () const
128 {
129  return d->connection ? d->connection->peerPort() : 0;
130 }
131 
133 {
134  return d->connection ? d->connection->peerName() : QStringLiteral("localhost");
135 }
136 
137 // --------------------- Sending messages
138 
140 {
141  if (!d->connection)
142  {
143  qCWarning(GAMES_PRIVATE_KGAME) << ": We have no connection yet!";
144  return;
145  }
146  d->connection->send (msg);
147 }
148 
150 {
151  QByteArray sendBuffer;
152  QBuffer buffer (&sendBuffer);
153  buffer.open (QIODevice::WriteOnly);
154  QDataStream stream (&buffer);
155 
156  stream << static_cast<quint32> ( KMessageServer::REQ_BROADCAST );
157  buffer.QIODevice::write (msg);
158  sendServerMessage (sendBuffer);
159 }
160 
161 void KMessageClient::sendForward (const QByteArray &msg, const QList <quint32> &clients)
162 {
163  QByteArray sendBuffer;
164  QBuffer buffer (&sendBuffer);
165  buffer.open (QIODevice::WriteOnly);
166  QDataStream stream (&buffer);
167 
168  stream << static_cast<quint32>( KMessageServer::REQ_FORWARD ) << clients;
169  buffer.QIODevice::write (msg);
170  sendServerMessage (sendBuffer);
171 }
172 
173 void KMessageClient::sendForward (const QByteArray &msg, quint32 client)
174 {
175  sendForward (msg, QList <quint32> () << client);
176 }
177 
178 
179 // --------------------- Receiving and processing messages
180 
182 {
183  if (d->isLocked)
184  {
185  d->delayedMessages.append(msg);
186  return;
187  }
188  if (!d->delayedMessages.isEmpty())
189  {
190  d->delayedMessages.append (msg);
191  QByteArray first = d->delayedMessages.front();
192  d->delayedMessages.pop_front();
193  processMessage (first);
194  }
195  else
196  {
197  processMessage(msg);
198  }
199 }
200 
202 {
203  if (d->isLocked)
204  { // must NOT happen, since we check in processIncomingMessage as well as in processFirstMessage
205  d->delayedMessages.append(msg);
206  return;
207  }
208  QBuffer in_buffer;
209  in_buffer.setData(msg);
210  in_buffer.open (QIODevice::ReadOnly);
211  QDataStream in_stream (&in_buffer);
212 
213 
214  bool unknown = false;
215 
216  quint32 messageID;
217  in_stream >> messageID;
218  switch (messageID)
219  {
220  case KMessageServer::MSG_BROADCAST:
221  {
222  quint32 clientID;
223  in_stream >> clientID;
224  Q_EMIT broadcastReceived (in_buffer.readAll(), clientID);
225  }
226  break;
227 
228  case KMessageServer::MSG_FORWARD:
229  {
230  quint32 clientID;
232  in_stream >> clientID >> receivers;
233  Q_EMIT forwardReceived (in_buffer.readAll(), clientID, receivers);
234  }
235  break;
236 
237  case KMessageServer::ANS_CLIENT_ID:
238  {
239  bool old_admin = isAdmin();
240  quint32 clientID;
241  in_stream >> clientID;
242  d->connection->setId (clientID);
243  if (old_admin != isAdmin())
245  }
246  break;
247 
248  case KMessageServer::ANS_ADMIN_ID:
249  {
250  bool old_admin = isAdmin();
251  in_stream >> d->adminID;
252  if (old_admin != isAdmin())
254  }
255  break;
256 
257  case KMessageServer::ANS_CLIENT_LIST:
258  {
259  in_stream >> d->clientList;
260  }
261  break;
262 
263  case KMessageServer::EVNT_CLIENT_CONNECTED:
264  {
265  quint32 id;
266  in_stream >> id;
267 
268  if (d->clientList.contains (id))
269  qCWarning(GAMES_PRIVATE_KGAME) << ": Adding a client that already existed!";
270  else
271  d->clientList.append (id);
272 
274  }
275  break;
276 
277  case KMessageServer::EVNT_CLIENT_DISCONNECTED:
278  {
279  quint32 id;
280  qint8 broken;
281  in_stream >> id >> broken;
282 
283  if (!d->clientList.contains (id))
284  qCWarning(GAMES_PRIVATE_KGAME) << ": Removing a client that doesn't exist!";
285  else
286  d->clientList.removeAll (id);
287 
288  Q_EMIT eventClientDisconnected (id, bool (broken));
289  }
290  break;
291 
292  default:
293  unknown = true;
294  }
295 
296  if (!unknown && !in_buffer.atEnd())
297  qCWarning(GAMES_PRIVATE_KGAME) << ": Extra data received for message ID" << messageID;
298 
299  Q_EMIT serverMessageReceived (msg, unknown);
300 
301  if (unknown)
302  qCWarning(GAMES_PRIVATE_KGAME) << ": received unknown message ID" << messageID;
303 }
304 
306 {
307  if (d->isLocked)
308  {
309  return;
310  }
311  if (d->delayedMessages.count() == 0)
312  {
313  qCDebug(GAMES_PRIVATE_KGAME) << ": no messages delayed";
314  return;
315  }
316  QByteArray first = d->delayedMessages.front();
317  d->delayedMessages.pop_front();
318  processMessage (first);
319 }
320 
322 {
323  qCDebug(GAMES_PRIVATE_KGAME) << ": timer single shot for removeBrokenConnection"<<this;
324  // MH We cannot directly delete the socket. otherwise QSocket crashes
325  QTimer::singleShot( 0, this, &KMessageClient::removeBrokenConnection2 );
326  return;
327 }
328 
329 
330 void KMessageClient::removeBrokenConnection2 ()
331 {
332  qCDebug(GAMES_PRIVATE_KGAME) << ": Broken:Deleting the connection object"<<this;
333 
335  delete d->connection;
336  d->connection = nullptr;
337  d->adminID = 0;
339  qCDebug(GAMES_PRIVATE_KGAME) << ": Broken:Deleting the connection object DONE";
340 }
341 
343 {
344  qCDebug(GAMES_PRIVATE_KGAME) << ": Disconnect:Deleting the connection object";
345 
347  delete d->connection;
348  d->connection = nullptr;
349  d->adminID = 0;
351  qCDebug(GAMES_PRIVATE_KGAME) << ": Disconnect:Deleting the connection object DONE";
352 }
353 
355 {
356  d->isLocked = true;
357 }
358 
360 {
361  d->isLocked = false;
362  for (int i = 0; i < d->delayedMessages.count(); i++)
363  {
365  }
366 }
367 
369 {
370  return d->delayedMessages.count();
371 }
372 
373 
virtual void processIncomingMessage(const QByteArray &msg)
This slot is called from the signal KMessageIO::received whenever a message from the KMessageServer a...
bool isAdmin() const
quint16 peerPort() const
unsigned int delayedMessageCount() const
This class implements the message communication using a TCP/IP socket.
Definition: kmessageio.h:175
void sendForward(const QByteArray &msg, const QList< quint32 > &clients)
Sends a message to all the clients in a list.
void sendServerMessage(const QByteArray &msg)
Sends a message to the KMessageServer.
void received(const QByteArray &msg)
This signal is emitted when /e send() on the connected KMessageIO object is called.
void aboutToDisconnect(quint32 id)
This signal is emitted right before the client disconnects.
void setData(const QByteArray &data)
quint32 adminId() const
KMessageClient(QObject *parent=nullptr)
Constructor.
void forwardReceived(const QByteArray &msg, quint32 senderID, const QList< quint32 > &receivers)
This signal is emitted when the client receives a forward message from the KMessageServer, sent by another client.
bool isConnected() const
QList< quint32 > clientList() const
void serverMessageReceived(const QByteArray &msg, bool &unknown)
This signal is emitted on every message that came from the server.
This class implements the message communication using function calls directly.
Definition: kmessageio.h:299
void addClient(KMessageIO *)
Adds a new KMessageIO object to the communication server.
virtual bool atEnd() const const override
virtual void removeBrokenConnection()
This slot is called from the signal KMessageIO::connectionBroken.
void processFirstMessage()
Called from unlock() (using QTimer::singleShot) until all delayed messages are delivered.
void adminStatusChanged(bool isAdmin)
This signal is emitted when this client becomes the admin client or when it loses the admin client st...
void disconnect()
Corresponds to setServer(0); but also emits the connectionBroken signal.
void sendBroadcast(const QByteArray &msg)
Sends a message to all the clients connected to the server, including ourself.
This abstract base class represents one end of a message connections between two clients.
Definition: kmessageio.h:62
bool isNetwork() const
QByteArray readAll()
A server for message sending and broadcasting, using TCP/IP connections.
virtual bool open(QIODevice::OpenMode flags) override
void eventClientConnected(quint32 clientID)
This signal is emitted when another client has connected to the server.
void broadcastReceived(const QByteArray &msg, quint32 senderID)
This signal is emitted when the client receives a broadcast message from the KMessageServer, sent by another client.
void connectionBroken()
This signal is emitted when the connection is closed.
void eventClientDisconnected(quint32 clientID, bool broken)
This signal is emitted when the server has lost the connection to one of the clients (This could be b...
void lock()
Once this function is called no message will be received anymore.
quint32 id() const
virtual void processMessage(const QByteArray &msg)
This slot is called from processIncomingMessage or processFirstMessage, depending on whether the clie...
void connectionBroken()
This signal is emitted when the connection to the KMessageServer is broken.
QString peerName() const
void unlock()
Deliver every message that was delayed by lock() and actually deliver all messages that get received ...
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setServer(const QString &host, quint16 port)
Connects the client to (another) server.
~KMessageClient()
Destructor.
Q_EMITQ_EMIT
int receivers(const char *signal) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Thu Nov 26 2020 22:36:18 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.