class dnm_packet {
public:
char *username;
char *data;
};
do i need to include some kind of header before that that has the size of the incoming class ??
for implementing this at the moment i just convert the class to a char* and then back again but im not sure if that is correct ??
Sending Class Data
hey,
what im wanting to know is what to do after i have setup my connection. I have the server receiving data and the client able to send and receive.
what im not sure about is for sending information. At the moment, when it is received by the server it is split up by a single char. what im wanting to do is send a class:
PsYvIsIoN
Yes, you will need some sort of header.
The process is often called serialization if you want to search/read about it.
No, char* casting a struct/class that contains pointers will not work correctly.
It will happen to not crash if you send the packet to the same application because the memory address will be valid. To another program (on the same or a different machine) it will crash.
The process is often called serialization if you want to search/read about it.
No, char* casting a struct/class that contains pointers will not work correctly.
It will happen to not crash if you send the packet to the same application because the memory address will be valid. To another program (on the same or a different machine) it will crash.
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Here’s how I do it… first create an interface class like so:
I won’t go into much detail about this class… it should be easy to see what’s going on here. One thing I will mention is that the memcpy of the struct is my friend Brian’s ideal. It seems to work rather well… and is a simple solution to serialization of data. You aren’t really sending the class over the wire, your sending the data that can reconstruct this class on the other side. And that’s where class factories come into play.
That is a simple class factory. There are better ways of doing this – what you see above is the easy and simple method that you should be able to derive your class factory from. There are many articles on the net that can instruct you with the finer points like maps or storing the objects in DLL’s etc…
Essentially, when we get a message in from the wire we pull the first DWORD off of it and that is our message id. We pass that message id to the class factory and receive and object of the type of class that that message pertains to. We then call object-> SerializeFrom() with the remaining data. Like so:
The message comes in and we pass it to our ProcessIncomingData() function.
It’s not magic… and sometimes it’s not pretty. But this works and works really well.
There’s a document on this site titled “Why Class Factories Rock My Multiplayer World” – look for it and it should help fill in some of the holes that I have left in this brief overview.
Good Luck,
Dave "Dak Lozar" Loeser
class INetMessage{protected: DWORD m_from;public: virtual int SerializeTo(char* pOutput) {return 0;} virtual void SerializeFrom(char* pFromData, int datasize) {} virtual void Exec() = 0; DWORD GetFrom() {return m_from;} void SetFrom(DWORD id) {m_from = id;} virtual void destroy() = 0;};[SOURCE]All of the network messages are derived from this class. The Exec() function is pure virtual, SerializeTo() and SerializeFrom() are overloaded in the derived class.The m_from holds the IP address of the sender and is maintained here so that we can reply. GetFrom() and SetFrom() are accessor function to m_from.Here is an example of one of those network classes:[SOURCE]class NMCLogin : public INetMessage{public: struct NMCLoginData { DWORD message_id; char account_name[51]; char password[51]; NMCLoginData() {message_id = 0x01;} };private: NMCLoginData m_data;public: void SetUserPass(char* user, char* pass) { strncpy(m_data.account_name, user, sizeof(m_data.account_name)); strncpy(m_data.password, pass, sizeof(m_data.password)); m_data.account_name[sizeof(m_data.account_name) - 1] = NULL; m_data.password[sizeof(m_data.password) - 1] = NULL; } int SerializeTo(char* pOutput) { memcpy(pOutput, &m_data, sizeof(NMCLoginData)); return sizeof(NMCLoginData); } void SerializeFrom(char* pFromData, int datasize) { char *m_ptr = (((char*)&m_data) + sizeof(DWORD)); memcpy(m_ptr, pFromData, (sizeof(NMCLoginData) - sizeof(DWORD))); m_data.account_name[sizeof(m_data.account_name) - 1] = NULL; m_data.password[sizeof(m_data.password) - 1] = NULL; } void Exec(); void destroy() {delete this;}};
I won’t go into much detail about this class… it should be easy to see what’s going on here. One thing I will mention is that the memcpy of the struct is my friend Brian’s ideal. It seems to work rather well… and is a simple solution to serialization of data. You aren’t really sending the class over the wire, your sending the data that can reconstruct this class on the other side. And that’s where class factories come into play.
class NetMessageFactory{public: INetMessage* instantiate(char* req_class_name) { if (strcmp(req_class_name, "NMCLogin") == 0) return new NMCLogin; } INetMessage* instantiate(long req_class_id) { switch(req_class_id) { case 0x0A0A: return new NMCLogin; break; } }};
That is a simple class factory. There are better ways of doing this – what you see above is the easy and simple method that you should be able to derive your class factory from. There are many articles on the net that can instruct you with the finer points like maps or storing the objects in DLL’s etc…
Essentially, when we get a message in from the wire we pull the first DWORD off of it and that is our message id. We pass that message id to the class factory and receive and object of the type of class that that message pertains to. We then call object-> SerializeFrom() with the remaining data. Like so:
void Network::ProcessIncomingData(char* datastream, WORD result, ULONG address, DWORD tick ){ OutputDebugString( "> EPUDPNetwork::ProcessIncomingData()\n" ); DWORD MSGID = *(DWORD*)datastream; datastream += sizeof(DWORD); INetMessage* imsg = m_NMFactory.instantiate(MSGID); imsg->SerializeFrom(datastream, result-sizeof(DWORD)); imsg->SetFrom(address); imsg->Exec(); imsg->destroy();}
The message comes in and we pass it to our ProcessIncomingData() function.
It’s not magic… and sometimes it’s not pretty. But this works and works really well.
There’s a document on this site titled “Why Class Factories Rock My Multiplayer World” – look for it and it should help fill in some of the holes that I have left in this brief overview.
Good Luck,
Dave "Dak Lozar" Loeser
Dave Dak Lozar Loeser
"Software Engineering is a race between the programmers, trying to make bigger and better fool-proof software, and the universe trying to make bigger fools. So far the Universe in winning."--anonymous
"Software Engineering is a race between the programmers, trying to make bigger and better fool-proof software, and the universe trying to make bigger fools. So far the Universe in winning."--anonymous
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement
Recommended Tutorials
Advertisement