c++ : How to send a struct from client to server through socket and serialize it?

Viewed 1241

I developed a c++ program for communication between client and server through socket. They are sending to each other string messages.

On the other hand, I developed a c++ program for serializing data into human readable form.

My problem is, how to modify those programs in order to send a structure from client to server and then the server serialize it and save it into a file ?

This is the server program :

#include <iostream>
#include <boost/asio.hpp>

using namespace boost::asio;
using ip::tcp;
using std::string;
using std::cout;
using std::endl;

string read_(tcp::socket & socket) {
       boost::asio::streambuf buf;
       boost::asio::read_until( socket, buf, "\n" );
       string data = boost::asio::buffer_cast<const char*>(buf.data());
       return data;
}
void send_(tcp::socket & socket, const string& message) {
       const string msg = message + "\n";
       boost::asio::write( socket, boost::asio::buffer(message) );
}

int main() {
      boost::asio::io_service io_service;
//listen for new connection
      tcp::acceptor acceptor_(io_service, tcp::endpoint(tcp::v4(), 1234 ));
//socket creation 
      tcp::socket socket_(io_service);
//waiting for connection
      acceptor_.accept(socket_);
//read operation
      string message = read_(socket_);
      cout << message << endl;
//write operation
      send_(socket_, "Hello From Server!");
      cout << "Servent sent Hello message to Client!" << endl;
   return 0;
}

This is the client program :

#include <iostream>
#include <boost/asio.hpp>

using namespace boost::asio;
using ip::tcp;
using std::string;
using std::cout;
using std::endl;

int main() {
     boost::asio::io_service io_service;
//socket creation
     tcp::socket socket(io_service);
//connection
     socket.connect( tcp::endpoint( boost::asio::ip::address::from_string("127.0.0.1"), 1234 ));
// request/message from client
     const string msg = "Hello from Client!\n";
     boost::system::error_code error;
     boost::asio::write( socket, boost::asio::buffer(msg), error );
     if( !error ) {
        cout << "Client sent hello message!" << endl;
     }
     else {
        cout << "send failed: " << error.message() << endl;
     }
 // getting response from server
    boost::asio::streambuf receive_buffer;
    boost::asio::read(socket, receive_buffer, boost::asio::transfer_all(), error);
    if( error && error != boost::asio::error::eof ) {
        cout << "receive failed: " << error.message() << endl;
    }
    else {
        const char* data = boost::asio::buffer_cast<const char*>(receive_buffer.data());
        cout << data << endl;
    }
    return 0;
}

This is how serialize strcuture :

const char* PERSON_FORMAT_OUT = "(%s, %d, %c)\n";
//const char* PERSON_FORMAT_IN = "(%[^,], %d, %c)\n";

typedef struct Person {
    char name[20];
    int age;
    char gender;
} Person;

int main (int argc, char* argv[]) {
    Person p1 = {
        .name = "Andrew",
        .age = 22,
        .gender = 'M'
    };

    //Person p2;

FILE* file;

fopen_s(&file, "people.dat", "w+");
fseek(file, 0, SEEK_SET);

fprintf_s(file, PERSON_FORMAT_OUT, p1.name, p1.age, p1.gender);

//fscanf_s(file, PERSON_FORMAT_IN, p2.name, 20, &p2.age, &p2.gender);

return 0;
}
5 Answers

If you intend to have common structures definitions so that both client and server can exchange information using the same format, I would recommend using Protocol Buffers (https://developers.google.com/protocol-buffers). You can generate a .proto file with the data definitions and share it between client/server code. Another option is to use gRPC (https://grpc.io/).

You can send any characters over the socket, so you could even define your own communication protocol with no standard structure as you did above. Another approach would be to define your data structure in JSON/XML and use a third-party library such as https://github.com/nlohmann/json to create and parse the structures. You could then convert these structures into STL strings or string streams and transport those over your socket connection.

The main drawback of going this way, and not the protobuf or grpc way, is that any time you need to change the structure definition you will need to update both your client and server code. You also get free goodies from using these, as they are more flexible, maintainable and scale better if the communication interface feature set grows.

Best things for protocol is to use google protobufs

We solve this using:

There is a relatively new kid on the block that does excellent job of defining multi platform/language structures and manage its serialization Flatbuffers. In contrast with protobuf, it does not have packing/unpacking step. Generally, it saves one memory copy and trade off is extra code needed on construction of object. If you care about performance, gRPC and flatbuffers is a probably a best choice.

Related