Solved

Best way to read a class sent by winsock

Posted on 2007-11-16
12
299 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
Comment Utility
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
Comment Utility
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
Comment Utility
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
 

Author Comment

by:dead6re
Comment Utility
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
Comment Utility
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
Comment Utility
>> 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
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 

Author Comment

by:dead6re
Comment Utility
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
Comment Utility
A string or a bytestring ... your choice :)
0
 

Author Comment

by:dead6re
Comment Utility
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
Comment Utility
You can send anything, either binary or text, single or multiline.
0
 
LVL 40

Assisted Solution

by:evilrix
evilrix earned 120 total points
Comment Utility
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
Comment Utility
>> 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

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

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 …
Whether you’re a college noob or a soon-to-be pro, these tips are sure to help you in your journey to becoming a programming ninja and stand out from the crowd.
The goal of this video is to provide viewers with basic examples to understand and use conditional statements in the C programming language.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

763 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

Need Help in Real-Time?

Connect with top rated Experts

7 Experts available now in Live!

Get 1:1 Help Now