File indexing completed on 2024-06-23 04:04:20
0001 /* 0002 This file is part of the KDE games library 0003 SPDX-FileCopyrightText: 2001 Burkhard Lehner <Burkhard.Lehner@gmx.de> 0004 0005 SPDX-License-Identifier: LGPL-2.0-only 0006 */ 0007 0008 #ifndef __KMESSAGESERVER_H__ 0009 #define __KMESSAGESERVER_H__ 0010 0011 // own 0012 #include "kdegamesprivate_export.h" 0013 // Qt 0014 #include <QObject> 0015 // Std 0016 #include <memory> 0017 0018 class KMessageIO; 0019 class KMessageServerPrivate; 0020 0021 /** 0022 \class KMessageServer kmessageserver.h <KGame/KMessageServer> 0023 0024 @short A server for message sending and broadcasting, using TCP/IP connections. 0025 0026 An object of this class listens for incoming connections via TCP/IP sockets and 0027 creates KMessageSocket objects for every established connection. It receives 0028 messages from the "clients", analyzes them and processes an appropriate 0029 reaction. 0030 0031 You can also use other KMessageIO objects with KMessageServer, not only 0032 TCP/IP socket based ones. Use addClient to connect via an object of any 0033 KMessageIO subclass. (For clients within the same process, you can e.g. use 0034 KMessageDirect.) This object already has to be connected. 0035 0036 The messages are always packages of an arbitrary length. The format of the messages 0037 is given below. All the data is stored and received with QDataStream, to be 0038 platform independent. 0039 0040 Setting up a KMessageServer can be done like this: 0041 0042 \code 0043 KMessageServer *server = new KMessageServer (); 0044 server->initNetwork (TCP/IP-Portnumber); 0045 \endcode 0046 0047 Usually that is everything you will do. There are a lot of public methods to 0048 administrate the object (maximum number of clients, finding clients, removing 0049 clients, setting the admin client, ...), but this functionality can also 0050 be done by messages from the clients. So you can administrate the object completely 0051 on remote. 0052 0053 If you want to extend the Server for your own needs (e.g. additional message types), 0054 you can either create a subclass and overwrite the method processOneMessage. 0055 (But don't forget to call the method of the superclass!) Or you can connect to 0056 the signal messageReceived, and analyze the messages there. 0057 0058 Every client has a unique ID, so that messages can be sent to another dedicated 0059 client or a list of clients. 0060 0061 One of the clients (the admin) has a special administration right. Some of the 0062 administration messages can only be used with him. The admin can give the admin 0063 status to another client. You can send a message to the admin by using clientID 0. 0064 This is always interpreted as the admin client, of its real clientID. 0065 0066 Here is a list of the messages the KMessageServer understands: 0067 << means, the value is inserted into the QByteArray using QDataStream. The 0068 messageIDs (REQ_BROADCAST, ...) are of type quint32. 0069 0070 - QByteArray << static_cast<quint32>( REQ_BROADCAST ) << raw_data 0071 0072 When the server receives this message, it sends the following message to 0073 ALL connected clients (a broadcast), where the raw_data is left unchanged: 0074 QByteArray << static_cast <quint32>( MSG_BROADCAST ) << clientID << raw_data 0075 quint32 clientID; // the ID of the client that sent the broadcast request 0076 0077 - QByteArray << static_cast<quint32>( REQ_FORWARD ) << client_list << raw_data 0078 QValueList <quint32> client_list; // list of receivers 0079 0080 When the server receives this message, it sends the following message to 0081 the clients in client_list: 0082 QByteArray << static_cast<quint32>( MSG_FORWARD ) << senderID << client_list << raw_data 0083 quint32 senderID; // the sender of the forward request 0084 QValueList <quint32> client_list; // a copy of the receiver list 0085 0086 Note: Every client receives the message as many times as he is in the client_list. 0087 Note: Since the client_list is sent to all the clients, every client can see who else 0088 got the message. If you want to prevent this, send a single REQ_FORWARD 0089 message for every receiver. 0090 0091 - QByteArray << static_cast<quint32>( REQ_CLIENT_ID ) 0092 0093 When the server receives this message, it sends the following message to 0094 the asking client: 0095 QByteArray << static_cast<quint32>( ANS_CLIENT_ID ) << clientID 0096 quint32 clientID; // The ID of the client who asked for it 0097 0098 Note: This answer is also automatically sent to a new connected client, so that he 0099 can store his ID. The ID of a client doesn't change during his lifetime, and is 0100 unique for this KMessageServer. 0101 0102 - QByteArray << static_cast<quint32>( REQ_ADMIN_ID ) 0103 0104 When the server receives this message, it sends the following message to 0105 the asking client: 0106 QByteArray << ANS_ADMIN_ID << adminID 0107 quint32 adminID; // The ID of the admin 0108 0109 Note: This answer is also automatically sent to a new connected client, so that he 0110 can see if he is the admin or not. It will also be sent to all connected clients 0111 when a new admin is set (see REQ_ADMIN_CHANGE). 0112 0113 - QByteArray << static_cast<quint32>( REQ_ADMIN_CHANGE ) << new_admin 0114 quint32 new_admin; // the ID of the new admin, or 0 for no admin 0115 0116 When the server receives this message, it sets the admin to the new ID. If no client 0117 with that ID exists, nothing happens. With new_admin == 0 no client is a admin. 0118 ONLY THE ADMIN ITSELF CAN USE THIS MESSAGE! 0119 0120 Note: The server sends a ANS_ADMIN_ID message to every connected client. 0121 0122 - QByteArray << static_cast<quint32>( REQ_REMOVE_CLIENT ) << client_list 0123 QValueList <quint32> client_list; // The list of clients to be removed 0124 0125 When the server receives this message, it removes the clients with the ids stored in 0126 client_list, disconnecting the connection to them. 0127 ONLY THE ADMIN CAN USE THIS MESSAGE! 0128 0129 Note: If one of the clients is the admin himself, he will also be deleted. 0130 Another client (if any left) will become the new admin. 0131 0132 - QByteArray << static_cast<quint32>( REQ_MAX_NUM_CLIENTS ) << maximum_clients 0133 qint32 maximum_clients; // The maximum of clients connected, or infinite if -1 0134 0135 When the server receives this message, it limits the number of clients to the number given, 0136 or sets it unlimited for maximum_clients == -1. 0137 ONLY THE ADMIN CAN USE THIS MESSAGE! 0138 0139 Note: If there are already more clients, they are not affected. It only prevents new Clients 0140 to be added. To assure this limit, remove clients afterwards (REQ_REMOVE_CLIENT) 0141 0142 - QByteArray << static_cast<quint32>( REQ_CLIENT_LIST ) 0143 0144 When the server receives this message, it answers by sending a list of IDs of all the clients 0145 that are connected at the moment. So it sends the following message to the asking client: 0146 QByteArray << static_cast<quint32>( ANS_CLIENT_LIST ) << clientList 0147 QValueList <quint32> clientList; // The IDs of the connected clients 0148 0149 Note: This message is also sent to every new connected client, so that he knows the other 0150 clients. 0151 0152 There are two more messages that are sent from the server to the every client automatically 0153 when a new client connects or a connection to a client is lost: 0154 0155 QByteArray << static_cast<quint32>( EVNT_CLIENT_CONNECTED ) << clientID; 0156 quint32 clientID; // the ID of the new connected client 0157 0158 QByteArray << static_cast<quint32>( EVNT_CLIENT_DISCONNECTED ) << clientID; 0159 quint32 clientID; // the ID of the client that lost the connection 0160 quint8 broken; // 1 if the network connection was closed, 0 if it was disconnected 0161 // on purpose 0162 0163 0164 @author Andreas Beckermann <b_mann@gmx.de>, Burkhard Lehner <Burkhard.Lehner@gmx.de> 0165 */ 0166 class KDEGAMESPRIVATE_EXPORT KMessageServer : public QObject 0167 { 0168 Q_OBJECT 0169 0170 public: 0171 /** 0172 MessageIDs for messages from a client to the message server. 0173 */ 0174 enum { 0175 REQ_BROADCAST = 1, 0176 REQ_FORWARD, 0177 REQ_CLIENT_ID, 0178 REQ_ADMIN_ID, 0179 REQ_ADMIN_CHANGE, 0180 REQ_REMOVE_CLIENT, 0181 REQ_MAX_NUM_CLIENTS, 0182 REQ_CLIENT_LIST, 0183 REQ_MAX_REQ = 0xffff 0184 }; 0185 0186 /** 0187 * MessageIDs for messages from the message server to a client. 0188 */ 0189 enum { 0190 MSG_BROADCAST = 101, 0191 MSG_FORWARD, 0192 ANS_CLIENT_ID, 0193 ANS_ADMIN_ID, 0194 ANS_CLIENT_LIST, 0195 EVNT_CLIENT_CONNECTED, 0196 EVNT_CLIENT_DISCONNECTED, 0197 EVNT_MAX_EVNT = 0xffff 0198 }; 0199 0200 /** 0201 * Create a KGameNetwork object 0202 */ 0203 explicit KMessageServer(quint16 cookie = 42, QObject *parent = nullptr); 0204 0205 ~KMessageServer() override; 0206 0207 /** 0208 * Gives debug output of the game status 0209 */ 0210 virtual void Debug(); 0211 0212 //---------------------------------- TCP/IP server stuff 0213 0214 /** 0215 * Starts the Communication server to listen for incoming TCP/IP connections. 0216 * 0217 * @param port The port on which the service is offered, or 0 to let the 0218 * system pick a free port 0219 * @return true if it worked 0220 */ 0221 bool initNetwork(quint16 port = 0); 0222 0223 /** 0224 * Returns the TCP/IP port number we are listening to for incoming connections. 0225 * (This has to be known by other clients so that they can connect to us. It's 0226 * especially necessary if you used 0 as port number in initNetwork(). 0227 * @return the port number 0228 */ 0229 quint16 serverPort() const; 0230 0231 /** 0232 * Stops listening for connections. The already running connections are 0233 * not affected. 0234 * To listen for connections again call initNetwork again. 0235 */ 0236 void stopNetwork(); 0237 0238 /** 0239 * Are we still offer offering server connections? 0240 * @return true, if we are still listening to connections requests 0241 */ 0242 bool isOfferingConnections() const; 0243 0244 //---------------------------------- adding / removing clients 0245 0246 public Q_SLOTS: 0247 /** 0248 * Adds a new @ref KMessageIO object to the communication server. This "client" 0249 * gets a unique ID. 0250 * 0251 * This slot method is automatically called for any incoming TCP/IP 0252 * connection. You can use it to add other types of connections, e.g. 0253 * local connections (KMessageDirect) to the server manually. 0254 * 0255 * NOTE: The @ref KMessageIO object gets owned by the KMessageServer, 0256 * so don't delete or manipulate it afterwards. It is automatically deleted 0257 * when the connection is broken or the communication server is deleted. 0258 * So, add a @ref KMessageIO object to just ONE KMessageServer. 0259 */ 0260 void addClient(KMessageIO *); 0261 0262 /** 0263 * Removes the KMessageIO object from the client list and deletes it. 0264 * This destroys the connection, if it already was up. 0265 * Does NOT emit connectionLost. 0266 * Sends an info message to the other clients, that contains the ID of 0267 * the removed client and the value of the parameter broken. 0268 * 0269 * @param io the object to delete and to remove from the client list 0270 * @param broken true if the client has lost connection 0271 * Mostly used internally. You will probably not need this. 0272 */ 0273 void removeClient(KMessageIO *io, bool broken); 0274 0275 /** 0276 Deletes all connections to the clients. 0277 */ 0278 void deleteClients(); 0279 0280 private Q_SLOTS: 0281 /** 0282 * Removes the sender object of the signal that called this slot. It is 0283 * automatically connected to @ref KMessageIO::connectionBroken. 0284 * Emits @ref connectionLost (KMessageIO*), and deletes the @ref KMessageIO object. 0285 * Don't call it directly! 0286 */ 0287 void removeBrokenClient(); 0288 0289 public: 0290 /** 0291 * sets the maximum number of clients which can connect. 0292 * If this number is reached, no more clients can be added. 0293 * Setting this number to -1 means unlimited number of clients. 0294 * 0295 * NOTE: Existing connections are not affected. 0296 * So, clientCount > maxClients is possible, if there were already 0297 * more clients than allowed before reducing this value. 0298 * 0299 * @param maxnumber the number of clients 0300 */ 0301 void setMaxClients(int maxnumber); 0302 0303 /** 0304 * returns the maximum number of clients 0305 * 0306 * @return the number of clients 0307 */ 0308 int maxClients() const; 0309 0310 /** 0311 * returns the current number of connected clients. 0312 * 0313 * @return the number of clients 0314 */ 0315 int clientCount() const; 0316 0317 /** 0318 * returns a list of the unique IDs of all clients. 0319 */ 0320 QList<quint32> clientIDs() const; 0321 0322 /** 0323 * Find the @ref KMessageIO object to the given client number. 0324 * @param no the client number to look for, or 0 to look for the admin 0325 * @return address of the client, or 0 if no client with that number exists 0326 */ 0327 KMessageIO *findClient(quint32 no) const; 0328 0329 /** 0330 * Returns the clientID of the admin, if there is a admin, 0 otherwise. 0331 * 0332 * NOTE: Most often you don't need to know that id, since you can 0333 * use clientID 0 to specify the admin. 0334 */ 0335 quint32 adminID() const; 0336 0337 /** 0338 * Sets the admin to a new client with the given ID. 0339 * The old admin (if existed) and the new admin will get the ANS_ADMIN message. 0340 * If you use 0 as new adminID, no client will be admin. 0341 */ 0342 void setAdmin(quint32 adminID); 0343 0344 //------------------------------ ID stuff 0345 0346 /* 0347 * The unique ID of this game 0348 * 0349 * @return int id 0350 */ 0351 // int gameId() const; 0352 0353 /* 0354 * Application cookie. this identifies the game application. It 0355 * help to distinguish between e.g. KPoker and KWin4 0356 * 0357 * @return the application cookie 0358 */ 0359 // int cookie() const; 0360 0361 //------------------------------ Message stuff 0362 0363 public: 0364 /** 0365 * Sends a message to all connected clients. 0366 * The message is NOT translated in any way. This method calls 0367 * @ref KMessageIO::send for every client added. 0368 */ 0369 virtual void broadcastMessage(const QByteArray &msg); 0370 0371 /** 0372 * Sends a message to a single client with the given ID. 0373 * The message is NOT translated in any way. 0374 * If no client with the given id exists, nothing is done. 0375 * This is just a convenience method. You could also call 0376 * @ref findClient (id)->send(msg) manually, but this method checks for 0377 * errors. 0378 */ 0379 virtual void sendMessage(quint32 id, const QByteArray &msg); 0380 0381 /** 0382 * Sends a message to a list of clients. Their ID is given in ids. If 0383 * a client id is given more than once in the list, the message is also 0384 * sent several times to that client. 0385 * This is just a convenience method. You could also iterate over the 0386 * list of IDs. 0387 */ 0388 virtual void sendMessage(const QList<quint32> &ids, const QByteArray &msg); 0389 0390 protected Q_SLOTS: 0391 /** 0392 * This slot receives all the messages from the @ref KMessageIO::received signals. 0393 * It stores the messages in a queue. The messages are later taken out of the queue 0394 * by @ref getReceivedMessage. 0395 * 0396 * NOTE: It is important that this slot may only be called from the signal 0397 * @ref KMessageIO::received, since the sender() object is used to find out 0398 * the client that sent the message! 0399 */ 0400 virtual void getReceivedMessage(const QByteArray &msg); 0401 0402 /** 0403 * This slot is called whenever there are elements in the message queue. This queue 0404 * is filled by @ref getReceivedMessage. 0405 * This slot takes one message out of the queue and analyzes processes it, 0406 * if it recognizes it. (See message types in the description of the class.) 0407 * After that, the signal @ref messageReceived is emitted. Connect to that signal if 0408 * you want to process other types of messages. 0409 */ 0410 virtual void processOneMessage(); 0411 0412 //---------------------------- Signals 0413 0414 Q_SIGNALS: 0415 /** 0416 * A new client connected to the game 0417 * @param client the client object that connected 0418 */ 0419 void clientConnected(KMessageIO *client); 0420 0421 /** 0422 * A network connection got broken. Note that the client will automatically get deleted 0423 * after this signal is emitted. The signal is not emitted when the client was removed 0424 * regularly. 0425 * 0426 * @param client the client which left the game 0427 */ 0428 void connectionLost(KMessageIO *client); 0429 0430 /** 0431 * This signal is always emitted when a message from a client is received. 0432 * 0433 * You can use this signal to extend the communication server without subclassing. 0434 * Just connect to this signal and analyze the message, if unknown is true. 0435 * If you recognize a message and process it, set unknown to false, otherwise 0436 * a warning message is printed. 0437 * 0438 * @param data the message data 0439 * @param clientID the ID of the KMessageIO object that received the message 0440 * @param unknown true, if the message type is not known by the KMessageServer 0441 */ 0442 void messageReceived(const QByteArray &data, quint32 clientID, bool &unknown); 0443 0444 protected: 0445 /** 0446 * @return A unique number which can be used as the id of a @ref KMessageIO. It is 0447 * incremented after every call so if you need the id twice you have to save 0448 * it anywhere. It's currently used to initialize newly connected clients only. 0449 */ 0450 quint32 uniqueClientNumber() const; 0451 0452 private: 0453 std::unique_ptr<KMessageServerPrivate> const d; 0454 }; 0455 0456 #endif