DProtocol lets us quickly create and parse complex messages. It's a list of one or more DFields, each of which contains the data to be sent or received. Both the sender and the receiver append the same DFields to their DProtocols, and should append them in the same order so that they can understand each other's messages. For example, say you wanted to send one to five user names followed by an integer:
DProtocol myProtocol; DField *currentField; currentField = new DField("user names", DA_STRING); currentField->setFieldRange(1, 5); currentField->addString("Mary"); currentField->addString("Mike"); currentField->addString("Johnny"); myProtocol.appendField(currentField); currentField = new DField("important number", DA_INT32_T); currentField->addValue(12); myProtocol.appendField(currentField); DBuffer message = myProtocol.generateMessage(); myDDispatcher->sendMessage(message);Note that the descriptive names "user names" and "important number" aren't sent across the network. The resulting message can be sent with DClient or DDispatcher. Here is the code to parse the same message received from a DDispatcher:
void receiveHandler(DBuffer &theData) { DProtocol myProtocol; DField *currentField; string oneName; int32_t theNumber; currentField = new DField("the names", DA_STRING); currentField->setFieldRange(1, 5); myProtocol.appendField(currentField); myProtocol.appendField(new DField("the number", DA_INT32_T)); if (!myProtocol.parseMessage(theData)) cout << myProtocol.getErrMsg() << endl; else { oneField = myProtocol.getField("the names"); for (unsigned int i = 0; i < oneField.size(); i++) { oneField->getString(i, oneName); cout << oneName << endl; } oneField = myProtocol.getField("the number"); oneField->getValue(0, theNumber); cout << theNumber << endl; } }We can create fairly complex protocols with this scheme. Since a DField can contain multiple DBuffers, it's possible to create 'nested' protocols where one DProtocol contains DBuffers that are parsed by another DProtocol. Say if you had a game where the player's name, x, y and z coordinates were being sent - you could create a 'player' DField with one DBuffer per player. The DBuffers would contain each player's data, which would be individually parsed by a different DProtocol.
Default constructor, creates a new DProtocol.
Copy constructor, creates a DProtocol that's a copy of source.
Sets this DProtocol to be an exact duplicate of source.
Appends newField to the protocol. Both the sender and the receiver should add fields in the same order to understand each other's messages. newField should have a unique name within the DProtocol, which we will use to access it with getField(). The DProtocol takes ownership of newField, and will delete it automatically.
Generates a network message based on the current values in the DProtocol's fields. All values other than strings are sent in binary.
Returns a description of the most recent error in this DProtocol. This is typically set by parseMessage.
Returns the field with the specified fieldName. Throws an exception if no such field exists.
Parse a message that was generated with generateMessage. theMessage should contain the complete message we're expecting. Before parsing a message, be sure to add the expected fields with appendField. Returns false if theMessage was malformed, and sets errMsg with a description of the problem.