Advertisement

I this going to be ok as a Network message class

Started by February 12, 2015 07:45 AM
8 comments, last by hplus0603 9 years, 9 months ago

Hi.

Been at this all day, have learnt alot about boost binding and what not to do with a std::stringstream.

I first tryed haveing a std::stringstream in the network packet class that failed after hours of trying to get that to work,

I switched to a std:string.

and I have finaly got some thing that works I was wondering if I have forgot to add any thing.

Please have a quick look, And your advice will be welcome, thank in advance.

This is the base message class it holds a type. This type is what the message is something like chatMsg, or KIllUintMsg

These messages will be user define with the use of a DEFINE_USER_MSG_START any number after this the user can define for amessage type

all values before are reserved for system mesages(work in progress)

all message classes will need to derive from this class.

.


//------------------------------------------------------------------------------
//all net messages must be Derived from this base Class
//it holds a type
//-------------------------------------------------------------------------------
class cNetWorkMessageBase
{
	friend class boost::serialization::access;
    // When the class Archive corresponds to an output archive, the
    // & operator is defined similar to <<.  Likewise, when the class Archive
    // is a type of input archive the & operator is defined similar to >>.
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & Type; 
	
    }

public:

	cNetWorkMessageBase()
	{
		Type = -1;
	}
	~cNetWorkMessageBase(){}

	int32_t Type;//the type of the message all messages need the firts variable to be a type
};//end cNetWorkMessageBase
/////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

Ok this next class is the packet data that will be sent over the network

It use's boost serialization to serialize the the passed in message classes into a std::string

.



//------------------------------------------------------------------------
//we send this message to the network layer it holds the real message
//Serialized all messages will need to be derived from cNetWorkMessageBase
//the class cNetWorkMessageBase has a type member we deserialize to
//tell what the message is
//-------------------------------------------------------------------------
class cNetWorkPacket
{
	
	 std::string archive_stream;//hold the message in its serialized form
	 
public:
	cNetWorkPacket(const cNetWorkPacket &net)
	{
		archive_stream = net.archive_stream;
	}
	cNetWorkPacket(){}
	~cNetWorkPacket(){}


	//-------------------------------------------------------------
	//reset the data
	//--------------------------------------------------------------
	void Reset()
	{
		archive_stream = "";
	}//end Reset
	//////////////////////////////////////////////////////////////////



	 cNetWorkPacket& cNetWorkPacket::operator=( const cNetWorkPacket& other ) 
	 {
		 if(this == &other)
			 return *this;

		 archive_stream = other.archive_stream;
      return *this;
	}


	//-------------------------------------------------------------------------
	//returns the stream as a string
	//-------------------------------------------------------------------------
	void GetData(std::string &str)
	{
		str = archive_stream;
	}//end GetData
	/////////////////////////////////////////////////////////////////////////////


	//-------------------------------------------------------------------------
	//call only in the network layer
	//-------------------------------------------------------------------------
	void SetData(std::string &str)
	{
		archive_stream = str;
	}//end SetData
	/////////////////////////////////////////////////////////////////////////////



	//----------------------------------------------------------------------
	//serialize the message into its network form
	//-----------------------------------------------------------------------
	template <typename T>
	bool SerializeMessage(const T& t)
	{
		std::ostringstream  outstream;
		
		try
		{
			// Serialize the data
			boost::archive::text_oarchive archive(outstream);
			archive << t;
			archive_stream = outstream.str();
		}
		catch (std::exception& e)
		{
			return false;
		}

		if(archive_stream.empty())
			return false;

		return true;
	}//end SerializeMessage
	////////////////////////////////////////////////////////////////////////


	//----------------------------------------------------------------------
	//serialize the message into its network form
	//-----------------------------------------------------------------------
	template <typename T>
	bool DeSerializeMessage(T& t)
	{
		if(archive_stream.empty())
			return false;

		try
		{
			//DeSerialize the data 
			std::istringstream archivestream(archive_stream);

			boost::archive::text_iarchive archive(archivestream);
			archive >> t;
		}
		catch (std::exception& e)
		{
			return false;
		}
		
		return true;
	}//end DeSerializeMessage
	////////////////////////////////////////////////////////////////////////

	//-------------------------------------------------------------------
	//returns the messages type member
	//--------------------------------------------------------------------
	bool GetMessageType(cNetWorkMessageBase &type)
	{
		return DeSerializeMessage(type);
	}//end GetMessageType
	//////////////////////////////////////////////////////////////////////


};//end class cNetWorkPacket
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////





And this is my first message its a chat message class derived from the networkbase class

.


//--------------------------------------------------------------
//this class holds our message type we dont send this 
//we send the Serialized message based on this classes data
//---------------------------------------------------------------
class cMessage: public cNetWorkMessageBase
{
private:
	friend class boost::serialization::access;
    // When the class Archive corresponds to an output archive, the
    // & operator is defined similar to <<.  Likewise, when the class Archive
    // is a type of input archive the & operator is defined similar to >>.
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
		// invoke serialization of the base class 
		ar & boost::serialization::base_object<cNetWorkMessageBase>(*this);
		// save/load class member variables
		ar & Name;
    }

public:
	cMessage()
	{
		Type = 111;//message type
	}//end

	~cMessage()
	{
	}

	std::string Name;
	
};//end cMessage
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////



To use do this.

cMessage mychat;

mychat.Type = CHAT_MSG;

mychat.Name = "Hello network World";

you cant just send this, you first need to serialize the message

cNetWorkPacket packet;

packet.Serialize(mychat);

now we can pass the packet to the network class for proccessing.

I think this is some of my best work yet

next I need a app to network pass on type thing something to feed the network with app messages and back out also.

Some more reading.

You should take a look at https://developers.google.com/protocol-buffers/

It uses a domain-specific language to define your messages, and then pre-compiles it to C++ (or other languages). You'll end up with the corresponding message classes above, but it's much more convenient and readable, with less typing and less error-prone.

openwar - the real-time tactical war-game platform

Advertisement
Unfortunately, posting a bunch of code and saying "could you look at this" is unlikely to get good feedback. If you have a specific problem, and can post symptoms and the relevant code, you will get much better feedback.

Separately, if you can get string to work, you should be able to get stringstream to work; they are not that different.
If you want to use a container that's somehow "better" than stringstream along some axis, I'd look at std::vector<>.
std::string is not that great for network I/O in my opinion.
enum Bool { True, False, FileNotFound };

Hi.

I forgot to say the above code is 1 level above the socket send data.

the class cConnection has a std::vector<char> as the inbound data buffer.

I was looking for bad design in the messages class.

The thing is every doc I read about networking has a string or they use char[someamount] or void *.

The most recent doc was the boost tutorials there chat app is using char data_[header_length + max_body_length];//there good for example code may not be for production code.

You see its not so black and white.

With the string stream it was reportting errors on private traits none copyable I tried ss = ss1, ss << ss1 then gave up and move to string.

some times its not about errors just design.

Where I live is a small country town and there is no one I can talk about theses thing. Ive tryed but there idea of programming is putting a disc in a drive and pressing enter lol.

You see its not so black and white.


I am recommending against using std::string. I think char[fixed-size] is great! (It's similar to std::vector<char> with a reserved size, if you prefer that kind of thing.)
enum Bool { True, False, FileNotFound };

If you were going to make a custom packet in binary. you need to add a packet header in case data is corrupted, or hacked.

Usually the header includes

1. Message ID

2. Message type or class

3. Checksum

4. Actual data size

Advertisement

Hi.

I named the cNetWorkPacket wrong, the connection class was doing the serialization to its sendbuffer(now a std::vector<char>Out_bound_data), but moved it out to cNetWorkPacket which should be called cNetWorkMessageSerializer. its a layer between the apps message and the network buffer.

2 things I'm really bad at naming classes and writting posts.

If you were going to make a custom packet in binary. you need to add a packet header in case data is corrupted, or hacked.

Usually the header includes

1. Message ID

2. Message type or class

3. Checksum

4. Actual data size

The checksum is likely wasted data. It isn't necessary assuming that your transport layer is TCP or UDP, since those are already doing those checks. And a checksum is useless against an actual attacker.

Ah. I now can see why you prefer to use char[amount]. Espesially if you want to go a lockfree queue, its a trivial type.

If I was thinking about using a lockless queue, I would need to limit the size of all messages sent to what ever size I place in the char array.

What would be a good size, and what about chat messages, would I just split it up if the user writes a whole page of text.

I know they still wait and are hardware dependent and will use a spin lock.

I can say it was a good read about the topic.

What would be a good size, and what about chat messages, would I just split it up if the user writes a whole page of text


The IPv6 minimum MTU is 1280 bytes. Give some overhead for IP, UDP, and ISP shenanigans, and a maximum packet size of 1200 bytes seems reasonable.
If you go over that, you will have to implement support for fragmenting datagrams yourself.

Btw: The UDP checksum is only 16 bits, and not using a strong algorithm. The reason UDP works fine by itself has more to do with all the underlying layers having stronger checksums in transit.

I know they still wait and are hardware dependent and will use a spin lock.


A single-reader, single-writer, bounded-upper-size lockless FIFO queue does not use a spinlock, and are not hardware dependent (other than "writes are eventually visible, and never visible out-of-order.")
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement