首页 > 解决方案 > c ++:如何通过套接字将结构从客户端发送到服务器并序列化?

问题描述

我开发了一个 c++ 程序,用于通过套接字在客户端和服务器之间进行通信。他们正在互相发送字符串消息。

另一方面,我开发了一个用于将数据序列化为人类可读形式的 C++ 程序。

我的问题是,如何修改这些程序以便将结构从客户端发送到服务器,然后服务器对其进行序列化并将其保存到文件中?

这是服务器程序:

#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;
}

这是客户端程序:

#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;
}

这是序列化结构的方式:

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;
}

标签: c++socketsserialization

解决方案


如果您打算有共同的结构定义,以便客户端和服务器可以使用相同的格式交换信息,我建议使用协议缓冲区(https://developers.google.com/protocol-buffers)。您可以使用数据定义生成一个 .proto 文件,并在客户端/服务器代码之间共享它。另一种选择是使用 gRPC ( https://grpc.io/ )。

您可以通过套接字发送任何字符,因此您甚至可以像上面那样定义自己的通信协议,而无需标准结构。另一种方法是在 JSON/XML 中定义您的数据结构,并使用第三方库(例如https://github.com/nlohmann/json)来创建和解析结构。然后,您可以将这些结构转换为 STL 字符串或字符串流,并通过套接字连接传输它们。

采用这种方式而不是 protobuf 或 grpc 方式的主要缺点是,任何时候您需要更改结构定义时,都需要更新客户端和服务器代码。您还可以从使用这些中获得免费的好处,因为如果通信接口功能集增长,它们会更加灵活、可维护并且可以更好地扩展。


推荐阅读