Solved

Best way to read a class sent by winsock

Posted on 2007-11-16
12
303 Views
Last Modified: 2013-11-13
In C++ I have a very basic class like this:
class Test {
public:
      bool bTest;
};

This is sent using:
Test Tester;
Tester.Test = true;
std::string sTest = (char *)&Tester;
SendData(sTest);

What would be the best way to read this class in the Server? Would using a class deliminator and size be a good idea? How would you approach this problem?
0
Comment
Question by:dead6re
  • 4
  • 3
  • 3
  • +1
12 Comments
 
LVL 55

Expert Comment

by:Jaime Olivares
ID: 20301966
Will be clearer if you implement a Serialize function like:

class Test {
public:
      bool bTest;

      std::string Serialize()
      {
            std::string result = this.bTest ? "1" : "0";
            return result;
      }
};

So you can use as:
Test Tester;
Tester.Test = true;
std::string sTest = Tester.Serialize();
SendData(sTest);

The same approach for more complex classes.
0
 
LVL 53

Assisted Solution

by:Infinity08
Infinity08 earned 80 total points
ID: 20301981
That's not a good way to send data for a few reasons :

1) how do you know the size of the data to send ?
2) how can you be sure that the memory layout will be the same on the target machine ?
3) etc.

A lot better way to to this is to serialize the class, and then send it. Something like :


class Test {
  public :
    bool bTest;
    int serialize(unsigned char *buf, int size);
};
 
int Test::serialize(unsigned char *buf, int size) {
  // check size of buffer !!
  if (size < 1) return -1;
 
  // serialize the data
  buf[0] = bTest ? 0x01 : 0x00;
 
  // return the size
  return 1;
}
 
Test tester;
tester.bTest = true;
unsigned char buf[16] = { 0 };
int size = tester.serialize(buf, 16);
SendData(buf, size);

Open in new window

0
 
LVL 40

Accepted Solution

by:
evilrix earned 120 total points
ID: 20302417
When sending data over a socket you are just sending a stream of raw bytes. It is up to you to define a protocol for handling this, telling the remote end what you are about to send, how big it is and what ever error correction protocol you plan to use (e.g. CRC -- http://en.wikipedia.org/wiki/Cyclic_redundancy_check) to ensure that what was sent is what was received.

The best thing to do is to convert you class into a Plain Old Data (POD -- http://en.wikipedia.org/wiki/Plain_Old_Data_Structures), either as a raw buffer or as a struct that meets the POD criteria (I prefer structs as they are strongly typed and as such are more easy to manipulate using the C++ type system). Once you have a POD you can then send this as a stream of bytes that can be reassembled at the other end. Once the POD has been reassembled at the other end you can use this to reconstruct your class.

Below is a very simplified example of what's involved. I must stress this is NOT a solution, it is just to give you some idea. A full solution is far more involved.

I hope this helps.

-Rx.



//////////////////////////////////////////////////////////////////////////////
// POD transporter
struct podFoo
{
	size_t size;
	int n;
	char c;
	bool b;
};
//////////////////////////////////////////////////////////////////////////////
// Class to serialize
class foo
{
public:
	// Normal contructor
	foo() : n(99),c('a'),b(true){}
 
	// Reconstructor (from POD)
	foo(podFoo const & pf) : n(pf.n),c(pf.c),b(pf.b){}
 
	int get_n() const { return n; }
	char get_c() const { return c; }
	bool get_b() const { return b; }
private:
	int n;
	char c;
	bool b;
};
//////////////////////////////////////////////////////////////////////////////
// Overloaded seralization function
void operator << ( podFoo & pf, foo const & f)
{
	pf.size = sizeof(pf);
	pf.n = f.get_n();
	pf.c = f.get_c();
	pf.b = f.get_b();
}
 
void Local()
{
//////////////////////////////////////////////////////////////////////////////
// LOCAL END
 
// Obj we want to serialize
foo f;
 
// POD to serialized too
podFoo pf;
 
// Serialize
pf << f;
 
// Handshake with remote to let it know what is coming
 
// Send POD
SendData(reinterpret_cast<unsigned char *>(&pf), sizeof(pf));
 
// Generate and send CRC check data
//////////////////////////////////////////////////////////////////////////////
}
void Remote()
{
//////////////////////////////////////////////////////////////////////////////
// REMOTE END
 
// Handshake with local to find out what's coming
 
// Prepare to receive POD
podFoo pf;
 
// Receive POD
RecvData(reinterpret_cast<unsigned char *>(&pf), sizeof(pf));
 
// Generate CRC and compare with CRC received from local
 
// If CRC was good reconstruct foo class
foo f(pf);
//////////////////////////////////////////////////////////////////////////////
}

Open in new window

0
Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

 

Author Comment

by:dead6re
ID: 20303751
Okay, how can I approach serialization with the following classes:

class Player {
char* Name;
std::string Info;
bool Active;
}
0
 
LVL 40

Assisted Solution

by:evilrix
evilrix earned 120 total points
ID: 20303878
I have provided a working example below. Note, I've not implemented Winsock code, so I've just written dummy functions so the code will build.

Remote() and Local() are examples functions you'd need to implement. Main() shows the serialization and deserialization working.

As you can see it's not a small job. All this code for a small class with 2 members! Of course, if the classes you plan to serialize are similar you'd write reusable 'helper' code so make it simpler.

I hope this helps.

-Rx.
#include <string>
#include <sstream>
//////////////////////////////////////////////////////////////////////////////
// This are just to make it build -- these are PDK functions
inline void SendData(char const *, size_t){};
inline void RecvData(char const *, size_t){};
// This is just to make it build -- you'll need to calculate this!
size_t const size = 0;
//////////////////////////////////////////////////////////////////////////////
// POD transporter -- use a std::stringstream to make life easier
typedef std::stringstream SerializationBuffer;
//////////////////////////////////////////////////////////////////////////////
// Class to serialize
struct Player {
	Player() : Name("Hello"), Info("World"), Active(true){}
	Player(SerializationBuffer & sb)
	{
		size_t nameSize;
		sb >> nameSize;
 
		Name = new char[nameSize]; // Somewhere this needs deleting!
		sb.read(Name, static_cast<std::streamsize>(nameSize));
 
		size_t infoSize;
		sb >> infoSize;
 
		Info.resize(infoSize);
		sb.read(&Info[0], static_cast<std::streamsize>(infoSize));
 
		sb >> Active;
	}
	char* Name; // It was be better if this was also a std::string!
	std::string Info;
	bool Active;
};
//////////////////////////////////////////////////////////////////////////////
// Overloaded seralization function
void operator << (SerializationBuffer & sb, Player const & pl)
{
	size_t nameSize = strlen(pl.Name) + 1; // +1 inc. null
	sb << nameSize;
	sb.write(pl.Name, static_cast<std::streamsize>(nameSize));
	sb << pl.Info.size();
	sb.write(pl.Info.data(), static_cast<std::streamsize>(pl.Info.size()));
	sb << pl.Active;
}
 
void Local()
{
//////////////////////////////////////////////////////////////////////////////
// LOCAL END
 
// Obj we want to serialize
Player pl;
 
// POD to serialized too
SerializationBuffer sb;
 
// Serialize
sb << pl;
 
// Handshake with remote to let it know what is coming
 
// Send POD
SendData(sb.str().data(), sb.str().size());
 
// Generate and send CRC check data
//////////////////////////////////////////////////////////////////////////////
}
void Remote()
{
//////////////////////////////////////////////////////////////////////////////
// REMOTE END
 
// Handshake with local to find out what's coming and it's "size"
 
// Prepare to receive serialiozed data
std::string data(size, 0);
 
// Receive POD
RecvData(&data[0], size);
 
// Generate CRC and compare with CRC received from local
 
// If CRC was good reconstruct foo class
SerializationBuffer sb(data);
Player pl(sb);
//////////////////////////////////////////////////////////////////////////////
}
 
int main()
{
	// Local end is something like this...
	Player pl;
	SerializationBuffer sb;
 
	// Remote end is something like this
	sb << pl;
	Player pl2(sb);
 
	return 0;
}

Open in new window

0
 
LVL 53

Assisted Solution

by:Infinity08
Infinity08 earned 80 total points
ID: 20303892
>> how can I approach serialization with the following classes:

As evilrix said : if you want to do it properly, you'll have to put some effort in this. Basically, you have to define a format of the serialized data that contains the three data values Name, Info and Active. One way of packing it all in a byte string is to first put the Name (strlen(Name) + 1 bytes terminated by a '\0'), then Info (Info.length() + 1 bytes terminated by a '\0'), and finally Active (1 byte that is either 1 or 0).
0
 

Author Comment

by:dead6re
ID: 20308112
Okay, presuming I have this correct, serializating my second class it sends a string along the lines of
Hello[null for rest of size]World[null for rest of size]1
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 20308124
A string or a bytestring ... your choice :)
0
 

Author Comment

by:dead6re
ID: 20308177
Serialization just looks like sending one string of data instead of sending many lines. I wanted to make sure that I fully understood the subject.
0
 
LVL 55

Expert Comment

by:Jaime Olivares
ID: 20308185
You can send anything, either binary or text, single or multiline.
0
 
LVL 40

Assisted Solution

by:evilrix
evilrix earned 120 total points
ID: 20308197
I'd be careful about just using NULL to delimit your data since your data might actually contain NULL and so you'd parse it wrongly. I'm not saying I8 is wrong, if your data is only text then this is probably safe, but if you are serializing raw data (that could contain NULL)  you'll need to be smarter than this. As you can see, in my example I passed the length of the data so I could deserialize it without any side effects from the actual content. There are no hard and fast rules to this, you know your data so do what suits it best.

Serialization is exactly that, taking a complex structure and serializing it... turning it from a structured object to a series of bytes -- or a string of bytes. Incidentally, try not to confuse a string (sequence) of raw data bytes with a string of characters that make up words. Although conceptually they are the same the semantics are very different!
0
 
LVL 53

Assisted Solution

by:Infinity08
Infinity08 earned 80 total points
ID: 20308213
>> I'm not saying I8 is wrong, if your data is only text then this is probably safe

I indeed assumed that in the example, Name and Info were text strings.


>> Serialization is exactly that, taking a complex structure and serializing it... turning it from a structured object to a series of bytes -- or a string of bytes.

The reason for this is that you can define the format of the serialized data, and it will be the same for every system that will send/receive that serialized data. It's platform independent (when defined correctly).
0

Featured Post

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
mapBully challenge 6 154
Best book to learn C++ 4 84
Error C2678: binary '!=': no operator found... 4 60
Message not shown 5 67
Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.

828 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question