I am totally new to parallel computing and Boost library. But in my current project, I need send/recv a vector contain serialized class objects and the size will be decided in run time. After read the boost::mpi and boost::serialization document, I find below code while search in Google and compiled it using vs2008 with no error.
#include <boost/mpi.hpp>
#include <iostream>
#include <vector>
namespace mpi = boost::mpi;
class gps_position
{
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & degrees;
ar & minutes;
ar & seconds;
}
public:
int degrees;
int minutes;
float seconds;
gps_position() {};
gps_position(int d, int m, float s) :
degrees(d), minutes(m), seconds(s)
{}
};
int main(int argc, char *argv[]) {
mpi::environment env(argc, argv);
mpi::communicator world;
if(world.rank() == 0) {
std::vector<gps_position> positions;
positions.push_back(gps_position(1, 2, 3.));
positions.push_back(gps_position(5, 6, 10.0));
std::cout<< "Sent GPS positions:"<<positions.size()<<std::endl;
world.send(1, 0, positions);
}
else {
std::vector<gps_position> positions;
world.recv(0, 0, positions);
std::cout << "Received GPS positions: "<<positions.size() << std::endl;
for(unsigned int i=0;i<positions.size(); i++) {
std::cout << positions[i].degrees << "\t"
<< positions[i].minutes << "\t"
<< positions[i].seconds << std::endl;
}
}
return 0;
}
However the program is not working properly. looks like the process1 can never receive the vector contain gps_position objects from process0. The output is
c:\mpi3>mpiexec -n 2 mpitest
Sent GPS positions:2
Received GPS positions: 0
I have modified the code to allow it pass single element instead whole vector and it works perfectly. So I have totally no idea about whats wrong with the original code. Is boost::mpi capable to pass this type of vector at all? Any suggestions are greatly appreciated.
Thank you all in advance
Zac
Boost says that it can handle vector...and your type itself it obviously can handle too. naivly i would it expect to work.
check out the following from the boost documentation
Send data to another process.
This routine executes a potentially blocking send with tag tag to the
process with rank dest. It can be received by the destination process
with a matching recv call.
The given value must be suitable for transmission over MPI. There are
several classes of types that meet these requirements:
Types with mappings to MPI data types: If is_mpi_datatype<T> is convertible to mpl::true_, then value will be transmitted using the
MPI data type get_mpi_datatype(). All primitive C++ data types that
have MPI equivalents, e.g., int, float, char, double, etc., have
built-in mappings to MPI data types. You may turn a Serializable type
with fixed structure into an MPI data type by specializing
is_mpi_datatype for your type.
Serializable types: Any type that provides the serialize() functionality required by the Boost.Serialization library can be
transmitted and received.
Packed archives and skeletons: Data that has been packed into an mpi::packed_oarchive or the skeletons of data that have been backed
into an mpi::packed_skeleton_oarchive can be transmitted, but will be
received as mpi::packed_iarchive and mpi::packed_skeleton_iarchive,
respectively, to allow the values (or skeletons) to be extracted by
the destination process.
Content: Content associated with a previously-transmitted skeleton can be transmitted by send and received by recv. The receiving process
may only receive content into the content of a value that has been
constructed with the matching skeleton.
For types that have mappings to an MPI data type (including the
concent of a type), an invocation of this routine will result in a
single MPI_Send call. For variable-length data, e.g., serialized types
and packed archives, two messages will be sent via MPI_Send: one
containing the length of the data and the second containing the data
itself. Note that the transmission mode for variable-length data is an
implementation detail that is subject to change.
Thank you all for your help.
Finally solved this problem by recompile it under VS 2010. Not sure the root reason though. I guess some mismatch in head files and libs?
Related
This question already has answers here:
How to use boost serialization for binary output?
(2 answers)
Closed 1 year ago.
I have succeeded to serialize my Employee object into file format using Boost library, and I want to send it through socket using TCP/IP communication protocol, but I guess that this protocol does not work for files.
I am thinking of serializing and deserializing the object into binary format but I did not know how to do it, I did not find examples on the internet, so would you please help me ?
I will share with you my code :
#include <fstream>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
using namespace std;
class Employee {
private:
friend class boost::serialization::access;
int id;
string name;
float salary;
template<class Archive>
void serialize(Archive &a, const unsigned version){
a & id & name & salary;
}
public:
Employee(){}
Employee(int i, string n, float s):id(i),name(n),salary(s)
{}
};
int main()
{
const string filename = "emp.dat";
Employee e1(11,"Harry",4500.00f);
Employee e2(22,"Ravi",8800.00f);
Employee e3(33,"Tim",6800.00f);
Employee e4(44,"Rajiv",3400.00f);
// Serialize and persist the object
{
std::ofstream outfile(filename);
boost::archive::text_oarchive archive(outfile);
archive << e1 << e2 << e3 << e4;
}
// Deserialize and restore the object
Employee restored_e1;
Employee restored_e2;
Employee restored_e3;
Employee restored_e4;
{
std::ifstream infile(filename);
boost::archive::text_iarchive archive(infile);
archive >> restored_e1 >> restored_e2
>> restored_e3 >> restored_e4;
}
return 0;
}
[Summary]
a) look for a std::stream compatible class that accepts a socket. (I think boost asio has one).
b) create a file handle and assign the socket handle to it.
c) caveat: The boost serialize Archive concept does not lend itself easily to what you are trying to do. See below.
[Detail]
Firstoff, what you are asking here has nothing to do with text or binary file format. Either would work in principle with tcp/ip. text serializes your data in human readable (text) form whereas binary would simply replace the readable (text) with a binary representation. This has nothing to do with where the data is going.
That said: What you are trying to do is serialize to a tcp/ip socket instead of a file. This is possible, although it is not immediately obvious how. boost serialize uses an Archive as a wrapper around a std::stream. If you have a stream that writes data to a tcp/ip socket rather than a file, than that would be what you wanted.
[My recommendation]
I'll start with c): caveat. The boost Archive concept does not lend itself easily to serialization via sockets. The reason is that usually you have exactly ONE Archive PER FILE. Translated to socket: You can serialize exactly ONE Archive PER Connection. You will have to close the connection and reopen it, if you need to serialize more than one "archive". closing and reopening takes time.
Given the fact that you also don't have a std::otcpstream ready, my own approach was to serialize my Archives to std::o/istringstream and send the entire string buffer via TCP/IP. Like this:
std::ostringstream out;
{
boost::text_archive outA(out);
outA << do some serialization;
}
send_via_tcp(out.str());
Note: The brackets here are important. The Archive destructor will write an 'end' tag to the stream, so unless the destructor gets called, the out.str() call will yield an incomplete string.
[serializing directly to TCP/IP]
That said, you could try to serialize directly to TCP/IP. Problem is, there is no std::stream class that serializes to a socket. You'd expect a std::otcpstream for example, but there is none (in std).
a) I think boost asio has a stream that uses TCP/IP so you can look there.
b) The second option is to open a socket and then create a file handle for this socket. The function _dup() accepts a socket as input (I think). That way you have a file handle (rather than a socket) and can create an ofstream/ifstream for it. Or perhaps create the ofstream/ifstream first and reassign the file handle with _dup.
I think this will cause all read/write calls to go to the TCP/IP stack instead of the file system so in theory you should be able to write/read an Archive (exactly one). But like I said above, I'd simply use the stringstream method, if your archives aren't too big.
I want to write an instance of a class that includes different data types into hard disk and read it whenever I need.
I used the below code to do this. The problem is that whenever I save the object into a file, it creates a file on the folder but it is just size of 1 KB. Also when I open the file from the same function that saves the file, I can read variables in the class, but when I move the read section to another function and open the file from there, variables cannot be read. How can I fix the problem? Thanks in advance.
Write to a file:
stream.open("configuration/KMeansModel.bin", std::ios::out | std::ios::binary);
stream.write((char *)& kmeans, sizeof(kmeans));
stream.close();
Read from the file:
KMeans::KMeans kmeans_(umapFeatureLabel_);
stream_.open("configuration/KMeansModel.bin", std::ios::in, std::ios::binary);
stream_.read((char *)& kmeans_, sizeof(kmeans_));
stream_.close();
Class definition:
class KMeans
{
private:
int m_K;
int m_iters;
int m_dimensions;
int m_total_features;
std::vector<Cluster> m_clusters;
std::unordered_map<std::string, std::string> m_umapFeatureLabel;
std::unordered_map<int, std::vector<std::vector<long double>>> m_umapClusterFeatureList;
int getNearestClusterId(Feature feature);
public:
KMeans::KMeans::KMeans();
KMeans(std::unordered_map<std::string, std::string>& umapFeatureLabel);
void run(std::vector<Feature>& allFeatures);
void predict(Feature feature);
void updateKMeans(std::vector<Feature>& allNewFeaturesRead);
std::string getLabelOfFeature(std::string feature);
};
The bad news:
Your file saving code uses function sizeof. Your data structure includes vector and map objects.
For example, as far as sizeof is concerned, a std::vector object takes 16 bytes, absolutely regardless of the number of elements. That's 8 bytes for the element count, plus 8 bytes for the pointer to the actual elements, assuming a 64 bits machine.
Say your vector has 100 elements, 8 bytes per element, and the elements are stored starting at memory address 424000. The write method will dutifully store into the file a) the number 100 and b) the number 424000; but it will make absolutely no attempt to save into the file memory locations from 424000 to 424800. For it has no way to know that 424000 is a pointer; that's just a number.
Hence, the file does not contain the information that is necessary to restore the vector state.
As mentioned in the comments above, the subject of saving complex pointer-based data structures into simple byte arrays for the purpose of file storage or network transmission is known as serialization or marshalling/unmarshalling.
It is a non obvious subject of its own, in the same way as sorting algorithms or matrix multiplication are non obvious subjects. It would probably take you a lot of time to come up with a properly debugged solution of your own, a solution that takes care of maintaining consistency between saving and restoring code, etc ...
The good news:
Serialization is a non-obvious subject, but it is also an old, well-known subject. So instead of painfully coming up with your own solution, you can rely on existing, publicly available code.
In similar fashion, the only situations where you would have to come up with your own matrix multiplication code is when:
you're doing it purely for fun and/or self-training
you are writing a PhD thesis on matrix multiplication
you're paid to write linear algebra code
Other than these, you would probably rely on say existing LAPACK code.
Regarding serialization, as hinted to by Botje in the comments above, the Boost web site provides a C++ serialization library, along with a suitable tutorial.
Sample code:
I am providing below a small code sample using the Boost library. A simple guinea pig object contains an integer value, a string and a map. Of course, I am shamelessly borrowing from the Boost tutorial.
We need to include a couple of header files:
#include <map>
#include <fstream>
#include <iostream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/map.hpp>
The object class, which pretends to store some token geographical info:
class CapitalMap
{
public:
CapitalMap(const std::string& myName, int myVersion) :
_name(myName), _version(myVersion)
{};
CapitalMap() = default; // seems required by serialization
inline void add(const std::string& country, const std::string& city)
{ _cmap[country] = city; }
void fdump(std::ostream& fh);
private:
std::string _name;
int _version;
std::map<std::string, std::string> _cmap;
friend class boost::serialization::access; // ALLOW FOR FILE ARCHIVAL
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar & _name;
ar & _version; // mind the name conflict with plain "version" argument
ar & _cmap;
}
};
A small debugging utility function:
void CapitalMap::fdump(std::ostream& ofh) // text dumping utility for debug
{
ofh << "CapitalMap name = \"" << _name << "\" version = " <<
_version << '\n';
for (const auto& pair : _cmap) {
auto country = pair.first; auto city = pair.second;
ofh << city << " is the capital of " << country << '\n';
}
}
Code to create the object, save it on disk, and (implicitely) deallocate it:
void buildAndSaveCapitalMap (const std::string& archiveName,
const std::string& mapName,
int version)
{
CapitalMap euroCapitals(mapName, version);
euroCapitals.add("Germany", "Berlin");
euroCapitals.add("France", "Paris");
euroCapitals.add("Spain", "Madrid");
euroCapitals.fdump(std::cout); // just for checking purposes
// save data to archive file:
std::ofstream ofs(archiveName);
boost::archive::text_oarchive oa(ofs);
oa << euroCapitals;
// ofstream connexion closed automatically here
// archive object deleted here - because going out of scope
// CapitalMap object deleted here - because going out of scope
}
Small main program to create the file and then restore the object state from that file:
int main(int argc, char* argv[])
{
const std::string archiveName{"capitals.dat"};
std::cout << std::endl;
buildAndSaveCapitalMap(archiveName, "EuroCapitals", 42);
// go restore our CapitalMap object to its original state:
CapitalMap cm; // object created in its default state
std::ifstream ifs(archiveName);
boost::archive::text_iarchive inAr(ifs);
inAr >> cm; // read back object ...
std::cout << std::endl;
cm.fdump(std::cout); // check that it's actually back and in good shape ...
std::cout << std::endl;
return 0;
}
The problem of maintaining consistency between saving and restoring code is brilliantly solved by altering the meaning of operator “&” according to the direction of travel.
Minor problems along the way:
on a Linux distro, you need to get packages: boost, boost-devel, boost-serialization
it seems the object class needs to have a default constructor
you need to include files such as “boost/serialization/map.hpp” manually
Program execution:
$ g++ serialw00.cpp -lboost_serialization -o ./serialw00.x
$ ./serialw00.x
CapitalMap name = "EuroCapitals" version = 42
Paris is the capital of France
Berlin is the capital of Germany
Madrid is the capital of Spain
CapitalMap name = "EuroCapitals" version = 42
Paris is the capital of France
Berlin is the capital of Germany
Madrid is the capital of Spain
$
More details here: SO_q_523872
We are rewriting our legacy code in C to C++. At the core of our system, we have a TCP client, which is connected to master. Master will be streaming messages continuously. Each socket read will result in say an N number of message of the format - {type, size, data[0]}.
Now we don't copy these messages into individual buffers - but just pass the pointer the beginning of the message, the length and shared_ptr to the underlying buffer to a workers.
The legacy C version was single threaded and would do an inplace NTOH conversion like below:
struct Message {
uint32_t something1;
uint16_t something2;
};
process (char *message)
Message *m = (message);
m->something1 = htonl(m->something1);
m->something2 = htons(m->something2);
And then use the Message.
There are couple of issues with following the logging in new code.
Since we are dispatching the messages to different workers, each worker doing an ntoh conversion will cause cache miss issues as the messages are not cache aligned - i.e there is no padding b/w the messages.
Same message can be handled by different workers - this is the case where the message needs to processed locally and also relayed to another process. Here the relay worker needs the message in original network order and the local work needs to convert to host order. Obviously as the message is not duplicated both cannot be satisfied.
The solutions that comes to my mind are -
Duplicate the message and send one copy for all relay workers if any. Do the ntoh conversion of all messages belonging to same buffer in the dispatcher itself before dispatching - say by calling a handler->ntoh(message); so that the cache miss issue is solved.
Send each worker the original copy. Each worker will copy the message to local buffer and then do ntoh conversion and use it. Here each worker can use a thread-specific (thread_local) static buffer as a scratch pad to copy the message.
Now my question is
Is the option 1 way of doing ntoh conversion - C++sy? I mean the alignment requirement of the structure will be different from the char buffer. (we havent had any issue with this yet.). Using scheme 2 should be fine in this case as the scratch buffer can have alignment of max_align_t and hence should typecastable to any structure. But this incur copying the entire message - which can be quite big (say few K size)
Is there a better way to handle the situation?
Your primary issue seems to be how to handle messages that come in misaligned. That is, if each message structure doesn't have enough padding on the end of it so that the following message is properly aligned, you can trigger misaligned reads by reinterpreting a pointer to the beginning of a message as an object.
We can get around this a number of ways, perhaps the simplest would be to ntoh based on a single-byte pointer, which is effectively always aligned.
We can hide the nasty details behind wrapper classes, which will take a pointer to the start of a message and have accessors that will ntoh the appropriate field.
As indicated in the comments, it's a requirement that offsets be determined by a C++ struct, since that's how the message is initially created, and it may not be packed.
First, our ntoh implementation, templated so we can select one by type:
template <typename R>
struct ntoh_impl;
template <>
struct ntoh_impl<uint16_t>
{
static uint16_t ntoh(uint8_t const *d)
{
return (static_cast<uint16_t>(d[0]) << 8) |
d[1];
}
};
template <>
struct ntoh_impl<uint32_t>
{
static uint32_t ntoh(uint8_t const *d)
{
return (static_cast<uint32_t>(d[0]) << 24) |
(static_cast<uint32_t>(d[1]) << 16) |
(static_cast<uint32_t>(d[2]) << 8) |
d[3];
}
};
template<>
struct ntoh_impl<uint64_t>
{
static uint64_t ntoh(uint8_t const *d)
{
return (static_cast<uint64_t>(d[0]) << 56) |
(static_cast<uint64_t>(d[1]) << 48) |
(static_cast<uint64_t>(d[2]) << 40) |
(static_cast<uint64_t>(d[3]) << 32) |
(static_cast<uint64_t>(d[4]) << 24) |
(static_cast<uint64_t>(d[5]) << 16) |
(static_cast<uint64_t>(d[6]) << 8) |
d[7];
}
};
Now we'll define a set of nasty macros that will automatically implement accessors for a given name by looking up the member with the matching name in the struct proto (a private struct to each class):
#define MEMBER_TYPE(MEMBER) typename std::decay<decltype(std::declval<proto>().MEMBER)>::type
#define IMPL_GETTER(MEMBER) MEMBER_TYPE(MEMBER) MEMBER() const { return ntoh_impl<MEMBER_TYPE(MEMBER)>::ntoh(data + offsetof(proto, MEMBER)); }
Finally, we have an example implementation of the message structure you have given:
class Message
{
private:
struct proto
{
uint32_t something1;
uint16_t something2;
};
public:
explicit Message(uint8_t const *p) : data(p) {}
explicit Message(char const *p) : data(reinterpret_cast<uint8_t const *>(p)) {}
IMPL_GETTER(something1)
IMPL_GETTER(something2)
private:
uint8_t const *data;
};
Now Message::something1() and Message::something2() are implemented and will read from the data pointer at the same offsets they wind up being in Message::proto.
Providing the implementation in the header (effectively inline) has the potential to inline the entire ntoh sequence at the call site of each accessor!
This class does not own the data allocation it is constructed from. Presumably you could write a base class if there's ownership-maintaining details here.
i am working on a network project of mine in order to learn more about networking and right now i have designed a simple protocol/structure that i fill and send to the server, the problem is that all vectors and probably arrays aswell are invalid on the server side.
im gonna try to explain it with code, its alot easier that way.
My protocol:
typedef struct NETWORK_PROTOCOL {
int packet_size;
int number_of_data_files;
std::vector<std::string> data_files;
}
so its a pretty simple protocol, and what i did is that i fill it with data and its completely valid on the client side, however as soon as i send it to the server and try to convert it back it the vector is invalid but the integers are still valid.
this is how i create and send the data from the client:
NETWORK_PROTOCOL Protocol;
//Fills protocol with data
int sendt = send(ClientSocket, (const char*)&Protocol, Protocol.packet_size, 0);
and when it hits the server i still get the full size of the data, but as i said earlier it does not convert back properly :/
Code on the server side that tries to cast it back:
NETWORK_PROTOCOL* Protocol;
iResult = recv(ClientSocket, buffer, BUFFLEN, 0);
//then i have some validation code to check if the whole packet arrived since its TCP
Protocol = reinterpret_cast<NETWORK_PROTOCOL*>(buffer);
//And now the vector is invalid :/
im not really sure how to fix this problem, i thought it would be easy to convert it back since it is the exact same data on both sides. Any help to fix this issue is greatly appreciated.
std::vector can't be transferred this way: internally it uses pointers, so you send only a pointer, without any actual information, and that pointer is not valid on the receiving side.
In order to send the contents of vector, you need to somehow serialize it (convert it to the representation in which it can be easily transferred). For example, you can use is Boost.Serialization
#include <sstream>
// include headers that implement a archive in simple text format
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/vector.hpp>
struct NETWORK_PROTOCOL
{
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)
{
ar & packet_size;
ar & number_of_data_files; // you don't actually need it
ar & data_files;
}
public:
int packet_size;
int number_of_data_files;
std::vector<std::string> data_files;
};
Now you can serialize it like this:
std::ostringstream ofs;
boost::archive::text_oarchive oa(ofs);
oa << protocol; // protocol is your instance of NETWORK_PROTOCOL, which you want to send
// and then you'll be able to get a buffer from ofs using ofs.str()
Deserialize it like this:
NETWORK_PROTOCOL protocol;
std::istringstream ifs(buf);
boost::archive::text_iarchive ia(ifs);
ia >> protocol;
For practical usages you may want to use binary archives instead. If you decide to go with boost.serialization, I recommend starting looking here.
You may also like Google Protocol Buffers: https://developers.google.com/protocol-buffers/docs/cpptutorial
This comment is longer than allowed. So I put it as an answer; although I think it answers partially.
To send all the data in one shipment wasted space and bandwidth, because you'd have to take a maximum for the number of names and their sizes. So I suggest you divide your transmission in phases.
In the first phase you send the number of filenames that you are transmitting. In this way you prepare the server for receiving n file names. Then in the second phase you make a loop divided in two transmissions. The first transmission you send the file name size, then you prepare a buffer for receiving the filename.
For these modes you only use basic types (size_t and char *).
On the server side you can build your vector<string>, if you want to give that illusion
I hope it helps you
I'm trying to serialize objects to send over network through a socket using only STL. I'm not finding a way to keep objects' structure to be deserialized in the other host. I tried converting to string, to char* and I've spent a long time searching for tutorials on the internet and until now I have found nothing.
Is there a way to do it only with STL?
Are there any good tutorials?
I am almost trying boost, but if there is how to do it with STL I'd like to learn.
You can serialize with anything. All serialization means is that you are converting the object to bytes so that you can send it over a stream (like an std::ostream) and read it with another (like an std::istream). Just override operator <<(std::ostream&, const T&) and operator >>(std::istream&, T&) where T is each of your types. And all the types contained in your types.
However, you should probably just use an already-existing library (Boost is pretty nice). There are tons of things that a library like Boost does for you, like byte-ordering, taking care of common objects (like arrays and all the stuff from the standard library), providing a consistent means of performing serialization and tons of other stuff.
My first question will be: do you want serialization or messaging ?
It might seem stupid at first, since you asked for serialization, but then I have always distinguished the two terms.
Serialization is about taking a snapshot of your memory and restoring it later on. Each object is represented as a separate entity (though they might be composed)
Messaging is about sending information from one point to another. The message usually has its own grammar and may not reflect the organization of your Business Model.
Too often I've seen people using Serialization where Messaging should have been used. It does not mean that Serialization is useless, but it does mean that you should think ahead of times. It's quite difficult to alter the BOM once you have decided to serialize it, especially if you decide to relocate some part of information (move it from one object to another)... because how then are you going to decode the "old" serialized version ?
Now that that's been cleared up...
... I will recommend Google's Protocol Buffer.
You could perfectly rewrite your own using the STL, but you would end up doing work that has already been done, and unless you wish to learn from it, it's quite pointless.
One great thing about protobuf is that it's language agnostic in a way: ie you can generate the encoder/decoder of a given message for C++, Java or Python. The use of Python is nice for message injection (testing) or message decoding (to check the output of a logged message). It's not something that would come easy were you to use the STL.
Serializing C++ Objects over a Network Socket
This is 6 years late but I just recently had this problem and this was one of the threads that I came across in my search on how to serialize object through a network socket in C++. This solution uses just 2 or 3 lines of code. There are a lot of answers that I found work but the easiest that I found was to use reinterpret_cast<obj*>(target) to convert the class or structure into an array of characters and feed it through the socket. Here's an example.
Class to be serialized:
/* myclass.h */
#ifndef MYCLASS_H
#define MYCLASS_H
class MyClass
{
public:
int A;
int B;
MyClass(){A=1;B=2;}
~MyClass(){}
};
#endif
Server Program:
/* server.cpp */
#include "myclass.h"
int main (int argc, char** argv)
{
// Open socket connection.
// ...
// Loop continuously until terminated.
while(1)
{
// Read serialized data from socket.
char buf[sizeof(MyClass)];
read(newsockfd,buf, sizeof(MyClass));
MyClass *msg = reinterpret_cast<MyClass*>(buf);
std::cout << "A = " << std::to_string(msg->A) << std::endl;
std::cout << "B = " << std::to_string(msg->B) << std::endl;
}
// Close socket connection.
// ...
return 0;
}
Client Program:
/* client.cpp */
#include "myClass.h"
int main(int argc, char *argv[])
{
// Open socket connection.
// ...
while(1)
{
printf("Please enter the message: ");
bzero(buffer,256);
fgets(buffer,255,stdin);
MyClass msg;
msg.A = 1;
msg.B = 2;
// Write serialized data to socket.
char* tmp = reinterpret_cast<char*>(&msg);
write(sockfd,tmp, sizeof(MyClass));
}
// Close socket connection.
// ...
return 0;
}
Compile both server.cpp and client.cpp using g++ with -std=c++11 as an option. You can then open two terminals and run both programs, however, start the server program before the client so that it has something to connect to.
Hope this helps.
I got it!
I used strinstream to serialize objects and I sent it as a message using the stringstream's method str() and so string's c_str().
Look.
class Object {
public:
int a;
string b;
void methodSample1 ();
void methosSample2 ();
friend ostream& operator<< (ostream& out, Object& object) {
out << object.a << " " << object.b; //The space (" ") is necessari for separete elements
return out;
}
friend istream& operator>> (istream& in, Object& object) {
in >> object.a;
in >> object.b;
return in;
}
};
/* Server side */
int main () {
Object o;
stringstream ss;
o.a = 1;
o.b = 2;
ss << o; //serialize
write (socket, ss.str().c_str(), 20); //send - the buffer size must be adjusted, it's a sample
}
/* Client side */
int main () {
Object o2;
stringstream ss2;
char buffer[20];
string temp;
read (socket, buffer, 20); //receive
temp.assign(buffer);
ss << temp;
ss >> o2; //unserialize
}
I'm not sure if is necessary convert to string before to serialize (ss << o), maybe is possible directly from char.
I think you should use google Protocol Buffers in your project.In network transport Protocol buffers have many advantages over XML for serializing structured data. Protocol buffers:
are simpler
are 3 to 10 times smaller
are 20 to 100 times faster
are less ambiguous
generate data access classes that are easier to use programmaticall
and so on. I think you need read https://developers.google.com/protocol-buffers/docs/overview about protobuf