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 #ifndef _KMESSAGEIO_H_ 13 #define _KMESSAGEIO_H_ 14 15 // own 16 #include "libkdegamesprivate_export.h" 17 // Qt 18 #include <QObject> 19 #include <QProcess> 20 #include <QString> 21 #include <QHostAddress> 22 #include <QLoggingCategory> 23 24 /* 25 This macro shouldn't be here ideally. Already declared in kgame.h, but throws error if not placed here. 26 */ 27 Q_DECLARE_LOGGING_CATEGORY(GAMES_PRIVATE_KGAME) 28 29 class QTcpSocket; 30 class KProcess; 31 32 33 /** 34 \class KMessageIO kmessageio.h <KGame/KMessageIO> 35 36 This abstract base class represents one end of a message connections 37 between two clients. Each client has one object of a subclass of 38 KMessageIO. Calling /e send() on one object will emit the signal 39 /e received() on the other object, and vice versa. 40 41 For each type of connection (TCP/IP socket, COM port, direct connection 42 within the same class) a subclass of KMessageIO must be defined that 43 implements the pure virtual methods /e send() and /e isConnected(), 44 and sends the signals. (See /e KMessageSocket for an example implementation.) 45 46 Two subclasses are already included: /e KMessageSocket (connection using 47 TCP/IP sockets) and /e KMessageDirect (connection using method calls, both 48 sides must be within the same process). 49 */ 50 51 class KDEGAMESPRIVATE_EXPORT KMessageIO : public QObject 52 { 53 Q_OBJECT 54 55 public: 56 /** 57 * The usual QObject constructor, does nothing else. 58 **/ 59 explicit KMessageIO (QObject *parent = nullptr); 60 61 /** 62 * The usual destructor, does nothing special. 63 **/ 64 ~KMessageIO () override; 65 66 /** 67 * The runtime identification 68 */ rtti()69 virtual int rtti() const {return 0;} 70 71 /** 72 * @return Whether this KMessageIO is a network IO or not. 73 **/ 74 //virtual bool isNetwork () const = 0; isNetwork()75 virtual bool isNetwork () const 76 { 77 qCCritical(GAMES_PRIVATE_KGAME) << "Calling PURE virtual isNetwork...BAD"; 78 return false; 79 } 80 81 /** 82 This method returns the status of the object, whether it is already 83 (or still) connected to another KMessageIO object or not. 84 85 This is a pure virtual method, so it has to be implemented in a subclass 86 of KMessageIO. 87 */ 88 //virtual bool isConnected () const = 0; isConnected()89 virtual bool isConnected () const 90 { 91 qCCritical(GAMES_PRIVATE_KGAME) << "Calling PURE virtual isConnected...BAD"; 92 return false; 93 } 94 95 /** 96 Sets the ID number of this object. This number can for example be used to 97 distinguish several objects in a server. 98 99 NOTE: Sometimes it is useful to let two connected KMessageIO objects 100 have the same ID number. You have to do so yourself, KMessageIO doesn't 101 change this value on its own! 102 */ 103 void setId (quint32 id); 104 105 /** 106 Queries the ID of this object. 107 */ 108 quint32 id (); 109 110 /** 111 @return 0 in the default implementation. Reimplemented in @ref KMessageSocket. 112 */ peerPort()113 virtual quint16 peerPort () const { return 0; } 114 115 /** 116 @return "localhost" in the default implementation. Reimplemented in @ref KMessageSocket 117 */ peerName()118 virtual QString peerName () const { return QStringLiteral("localhost"); } 119 120 121 Q_SIGNALS: 122 /** 123 This signal is emitted when /e send() on the connected KMessageIO 124 object is called. The parameter contains the same data array in /e msg 125 as was used in /e send(). 126 */ 127 void received (const QByteArray &msg); 128 129 /** 130 This signal is emitted when the connection is closed. This can be caused 131 by a hardware error (e.g. broken internet connection) or because the other 132 object was killed. 133 134 Note: Sometimes a broken connection can be undetected for a long time, 135 or may never be detected at all. So don't rely on this signal! 136 */ 137 void connectionBroken (); 138 139 public Q_SLOTS: 140 141 /** 142 This slot sends the data block in /e msg to the connected object, that will 143 emit /e received(). 144 145 For a concrete class, you have to subclass /e KMessageIO and overwrite this 146 method. In the subclass, implement this method as an ordinary method, not 147 as a slot! (Otherwise another slot would be defined. It would work, but uses 148 more memory and time.) See /e KMessageSocket for an example implementation. 149 */ 150 virtual void send (const QByteArray &msg) = 0; 151 152 protected: 153 quint32 m_id; 154 }; 155 156 157 /** 158 \class KMessageSocket kmessageio.h <KGame/KMessageIO> 159 160 This class implements the message communication using a TCP/IP socket. The 161 object can connect to a server socket, or can use an already connected socket. 162 */ 163 164 class KMessageSocket : public KMessageIO 165 { 166 Q_OBJECT 167 168 public: 169 /** 170 Connects to a server socket on /e host with /e port. host can be an 171 numerical (e.g. "192.168.0.212") or symbolic (e.g. "wave.peter.org") 172 IP address. You can immediately use the /e sendSystem() and 173 /e sendBroadcast() methods. The messages are stored and sent to the 174 receiver after the connection is established. 175 176 If the connection could not be established (e.g. unknown host or no server 177 socket at this port), the signal /e connectionBroken is emitted. 178 */ 179 KMessageSocket (const QString& host, quint16 port, QObject *parent = nullptr ); 180 181 /** 182 Connects to a server socket on /e host with /e port. You can immediately 183 use the /e sendSystem() and /e sendBroadcast() methods. The messages are 184 stored and sent to the receiver after the connection is established. 185 186 If the connection could not be established (e.g. unknown host or no server 187 socket at this port), the signal /e connectionBroken is emitted. 188 */ 189 KMessageSocket (const QHostAddress &host, quint16 port, QObject *parent = nullptr); 190 191 /** 192 Uses /e socket to do the communication. 193 194 The socket should already be connected, or at least be in /e connecting 195 state. 196 197 Note: The /e socket object is then owned by the /e KMessageSocket object. 198 So don't use it otherwise any more and don't delete it. It is deleted 199 together with this KMessageSocket object. (Use 0 as parent for the QSocket 200 object t ensure it is not deleted.) 201 */ 202 explicit KMessageSocket (QTcpSocket *socket, QObject *parent = nullptr); 203 204 /** 205 Uses the socket specified by the socket descriptor socketFD to do the 206 communication. The socket must already be connected. 207 208 This constructor can be used with a QServerSocket within the (pure 209 virtual) method /e newConnection. 210 211 Note: The socket is then owned by the /e KMessageSocket object. So don't 212 manipulate the socket afterwards, especially don't close it. The socket is 213 automatically closed when KMessageSocket is deleted. 214 */ 215 explicit KMessageSocket (int socketFD, QObject *parent = nullptr); 216 217 /** 218 Destructor, closes the socket. 219 */ 220 ~KMessageSocket () override; 221 222 /** 223 * The runtime identification 224 */ rtti()225 int rtti() const override {return 1;} 226 227 /** 228 @return The port that this object is connected to. See QSocket::peerPort 229 */ 230 quint16 peerPort () const override; 231 232 /** 233 @return The hostname this object is connected to. See QSocket::peerName. 234 */ 235 QString peerName () const override; 236 237 /** 238 @return TRUE as this is a network IO. 239 */ isNetwork()240 bool isNetwork() const override { return true; } 241 242 /** 243 Returns true if the socket is in state /e connected. 244 */ 245 bool isConnected () const override; 246 247 /** 248 Overwritten slot method from KMessageIO. 249 250 Note: It is not declared as a slot method, since the slot is already 251 defined in KMessageIO as a virtual method. 252 */ 253 void send (const QByteArray &msg) override; 254 255 protected Q_SLOTS: 256 virtual void processNewData (); 257 258 protected: 259 void initSocket (); 260 QTcpSocket *mSocket; 261 bool mAwaitingHeader; 262 quint32 mNextBlockLength; 263 264 bool isRecursive; // workaround for "bug" in QSocket, Qt 2.2.3 or older 265 }; 266 267 268 /** 269 \class KMessageDirect kmessageio.h <KGame/KMessageIO> 270 271 This class implements the message communication using function calls 272 directly. It can only be used when both sides of the message pipe are 273 within the same process. The communication is very fast. 274 275 To establish a communication, you have to create two instances of 276 KMessageDirect, the first one with no parameters in the constructor, 277 the second one with the first as parameter: 278 279 /code 280 KMessageDirect *peer1, *peer2; 281 peer1 = new KMessageDirect (); // unconnected 282 peer2 = new KMessageDirect (peer1); // connect with peer1 283 /endcode 284 285 The connection is only closed when one of the instances is deleted. 286 */ 287 288 class KMessageDirect : public KMessageIO 289 { 290 Q_OBJECT 291 292 public: 293 /** 294 Creates an object and connects it to the object given in the first 295 parameter. Use 0 as first parameter to create an unconnected object, 296 that is later connected. 297 298 If that object is already connected, the object remains unconnected. 299 */ 300 explicit KMessageDirect (KMessageDirect *partner = nullptr, QObject *parent = nullptr); 301 302 /** 303 Destructor, closes the connection. 304 */ 305 ~KMessageDirect () override; 306 307 /** 308 * The runtime identification 309 */ rtti()310 int rtti() const override {return 2;} 311 312 313 /** 314 @return FALSE as this is no network IO. 315 */ isNetwork()316 bool isNetwork() const override { return false; } 317 318 /** 319 Returns true, if the object is connected to another instance. 320 321 If you use the first constructor, the object is unconnected unless another 322 object is created with this one as parameter. 323 324 The connection can only be closed by deleting one of the objects. 325 */ 326 bool isConnected () const override; 327 328 /** 329 Overwritten slot method from KMessageIO. 330 331 Note: It is not declared as a slot method, since the slot is already 332 defined in KMessageIO as a virtual method. 333 */ 334 void send (const QByteArray &msg) override; 335 336 protected: 337 KMessageDirect *mPartner; 338 }; 339 340 /** 341 * \class KMessageProcess kmessageio.h <KGame/KMessageIO> 342 */ 343 class KMessageProcess : public KMessageIO 344 { 345 Q_OBJECT 346 347 public: 348 KMessageProcess(QObject *parent, const QString& file); 349 ~KMessageProcess() override; 350 bool isConnected() const override; 351 void send (const QByteArray &msg) override; 352 353 /** 354 @return FALSE as this is no network IO. 355 */ isNetwork()356 bool isNetwork() const override { return false; } 357 358 /** 359 * The runtime identification 360 */ rtti()361 int rtti() const override {return 3;} 362 363 364 365 public Q_SLOTS: 366 void slotReceivedStdout(); 367 void slotReceivedStderr(); 368 void slotProcessExited(int, QProcess::ExitStatus); 369 370 Q_SIGNALS: 371 void signalReceivedStderr(const QString &msg); 372 373 private: 374 QString mProcessName; 375 KProcess *mProcess; 376 QByteArray* mSendBuffer; 377 QByteArray mReceiveBuffer; 378 int mReceiveCount; 379 }; 380 381 #endif 382 383