超越原始的结构体——序列化c++设计消息协议(C++asio网络库相关)

如果服务器和客户端都是用C++语言写的就可以用序列化方法将数据存放到流或文件中
可以用boost自带的serialization::access
archive可以看成是文件或内存,是内存就可以通过tcp传输到客户端
&和<<,>>等价,表示数据写入

以下是用boost序列化库写的一个例子:

//SerilizationObject.cpp
class SBindName {
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 & m_bindName;
		//ar << m_bindName;
		//ar >> m_bindName;
  }
  std::string m_bindName;

public:
  SBindName(std::string name) : m_bindName(std::move(name)) {}
	SBindName() {}
  const std::string &bindName() const { return m_bindName; }
};

class SChatInfo {
  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 &m_chatInformation;
  }
  std::string m_chatInformation;

public:
  SChatInfo(std::string info) : m_chatInformation(std::move(info)) {}
	SChatInfo() {}
  const std::string &chatInformation() const { return m_chatInformation; }
};

class SRoomInfo {
public:
	SRoomInfo() {}
  SRoomInfo(std::string name, std::string info)
      : m_bind(std::move(name)), m_chat(std::move(info)) {}
	const std::string& name() const { return m_bind.bindName();}
	const std::string& information() const { return m_chat.chatInformation();}
private:

  friend class boost::serialization::access;
  template <class Archive>
  void serialize(Archive &ar, const unsigned int version) {
    ar &m_bind;
		ar & m_chat;
  }
	SBindName m_bind;
	SChatInfo m_chat;

};

#endif

SBindName m_bind和SChatInfo m_chat;定义成类可以直接进行序列化操作,可以脱离pod类型的限制,定义非常复杂的类

#ifndef FND_STRUCT_HEADER_H
#define FND_STRUCT_HEADER_H
#include <string>
struct Header {
	int bodySize;
	int type;
};

enum MessageType {
	MT_BIND_NAME = 1,
	MT_CHAT_INFO = 2,
	MT_ROOM_INFO = 3,
};

// client send
struct BindName {
	char name[32];
	int nameLen;
};

// client send
struct ChatInformation {
	char information[256];
	int infoLen;
};


// serversend
struct RoomInformation {
	BindName name;
	ChatInformation chat;
};

bool parseMessage(const std::string& input, int* type, std::string& outbuffer);
bool parseMessage2(const std::string& input, int* type, std::string& outbuffer);
#endif
//structHeader.cpp
#include "structHeader.h"
#include "SerilizationObject.h"
#include <cstdlib>
#include <cstring>
#include <iostream>
template <typename T> std::string seriliaze(const T &obj) {
  std::stringstream ss;
  boost::archive::text_oarchive oa(ss);
  oa & obj;
  return ss.str();
}

bool parseMessage2(const std::string& input, int* type, std::string& outbuffer) {
  auto pos = input.find_first_of(" ");
  if (pos == std::string::npos)
    return false;
  if (pos == 0)
    return false;
	// "BindName ok" -> substr -> BindName
  auto command = input.substr(0, pos);
  if (command == "BindName") {
    // we try to bind name
    std::string name = input.substr(pos + 1);
    if (name.size() > 32)
      return false;
    if (type)
      *type = MT_BIND_NAME;
    //SBindName bindInfo(std::move(name));
    outbuffer = seriliaze(SBindName(std::move(name)));
    return true;
  } else if (command == "Chat") {
    // we try to chat
    std::string chat = input.substr(pos + 1);
    if (chat.size() > 256)
      return false;
		outbuffer = seriliaze(SChatInfo(std::move(chat)));
//    ChatInformation info;
//    info.infoLen = chat.size();
//    std::memcpy(&(info.information), chat.data(), chat.size());
//    auto buffer = reinterpret_cast<const char *>(&info);
//    outbuffer.assign(buffer, buffer + sizeof(info));
    if (type)
      *type = MT_CHAT_INFO;
    return true;
  }
  return false;
}
// cmd messagebody
bool parseMessage(const std::string &input, int *type, std::string &outbuffer) {
  // input should be cmd body
  auto pos = input.find_first_of(" ");
  if (pos == std::string::npos)
    return false;
  if (pos == 0)
    return false;
	// "BindName ok" -> substr -> BindName
  auto command = input.substr(0, pos);
  if (command == "BindName") {
    // we try to bind name
    std::string name = input.substr(pos + 1);
    if (name.size() > 32)
      return false;
    if (type)
      *type = MT_BIND_NAME;
    BindName bindInfo;
    bindInfo.nameLen = name.size();
    std::memcpy(&(bindInfo.name), name.data(), name.size());
    auto buffer = reinterpret_cast<const char *>(&bindInfo);
    outbuffer.assign(buffer, buffer + sizeof(bindInfo));
    return true;
  } else if (command == "Chat") {
    // we try to chat
    std::string chat = input.substr(pos + 1);
    if (chat.size() > 256)
      return false;
    ChatInformation info;
    info.infoLen = chat.size();
    std::memcpy(&(info.information), chat.data(), chat.size());
    auto buffer = reinterpret_cast<const char *>(&info);
    outbuffer.assign(buffer, buffer + sizeof(info));
    if (type)
      *type = MT_CHAT_INFO;
    return true;
  }
  return false;
}

parseMessage2是序列化对数据操作,对oa的操作最终序化到ss流中

客户端代码部分:

//client.cpp
#include "chat_message.h"
#include "structHeader.h"
#include "SerilizationObject.h"

#include <boost/asio.hpp>

#include <deque>
#include <iostream>
#include <thread>

#include <cstdlib>
#include <cassert>

using boost::asio::ip::tcp;

using chat_message_queue = std::deque<chat_message>;


class chat_client {
public:
  chat_client(boost::asio::io_service &io_service,
              tcp::resolver::iterator endpoint_iterator)
      : io_service_(io_service), socket_(io_service) {
    do_connect(endpoint_iterator);
  }

  void write(const chat_message &msg) {
    io_service_.post([this, msg]() {
      bool write_in_progress = !write_msgs_.empty();
      write_msgs_.push_back(msg);
      if (!write_in_progress) {
        do_write();
      }
    });
  }

  void close() {
    io_service_.post([this]() { socket_.close(); });
  }

private:
  void do_connect(tcp::resolver::iterator endpoint_iterator) {
    boost::asio::async_connect(
        socket_, endpoint_iterator,
        [this](boost::system::error_code ec, tcp::resolver::iterator) {
          if (!ec) {
            do_read_header();
          }
        });
  }

  void do_read_header() {
    boost::asio::async_read(
        socket_,
        boost::asio::buffer(read_msg_.data(), chat_message::header_length),
        [this](boost::system::error_code ec, std::size_t /*length*/) {
          if (!ec && read_msg_.decode_header()) {
            do_read_body();
          } else {
            socket_.close();
          }
        });
  }

  void do_read_body() {
    boost::asio::async_read(
        socket_, boost::asio::buffer(read_msg_.body(), read_msg_.body_length()),
        [this](boost::system::error_code ec, std::size_t /*length*/) {
          if (!ec) {
            if (read_msg_.type() == MT_ROOM_INFO) {
              SRoomInfo info;
              std::stringstream ss(
                  std::string(read_msg_.body(),
                              read_msg_.body() + read_msg_.body_length()));
              boost::archive::text_iarchive ia(ss);
              ia & info;
              std::cout << "client: '";
              std::cout << info.name();
              std::cout << "' says '";
              std::cout << info.information();
              std::cout << "'\n";
            }
            do_read_header();
          } else {
            socket_.close();
          }
        });
  }

  void do_write() {
    boost::asio::async_write(
        socket_, boost::asio::buffer(write_msgs_.front().data(),
                                     write_msgs_.front().length()),
        [this](boost::system::error_code ec, std::size_t /*length*/) {
          if (!ec) {
            write_msgs_.pop_front();
            if (!write_msgs_.empty()) {
              do_write();
            }
          } else {
            socket_.close();
          }
        });
  }

private:
  boost::asio::io_service &io_service_;
  tcp::socket socket_;
  chat_message read_msg_;
  chat_message_queue write_msgs_;
};

int main(int argc, char *argv[]) {
  try {
    if (argc != 3) {
      std::cerr << "Usage: chat_client <host> <port>\n";
      return 1;
    }

    boost::asio::io_service io_service;

    tcp::resolver resolver(io_service);
    auto endpoint_iterator = resolver.resolve({argv[1], argv[2]});
    chat_client c(io_service, endpoint_iterator);

    std::thread t([&io_service]() { io_service.run(); });

    char line[chat_message::max_body_length + 1];
		// ctrl-d
    while (std::cin.getline(line, chat_message::max_body_length + 1)) {
      chat_message msg;
			auto type = 0;
			std::string input(line, line + std::strlen(line));
			std::string output;
			if(parseMessage2(input, &type, output)) {
				msg.setMessage(type, output.data(), output.size());
				c.write(msg);
				std::cout << "write message for server " << output.size() << std::endl;
			}
    }

    c.close();
    t.join();
  } catch (std::exception &e) {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}

用序列化方式安全性会比reintepret_cast更高

通过stringstream将内存信息转化出来

发布了143 篇原创文章 · 获赞 34 · 访问量 3013

猜你喜欢

转载自blog.csdn.net/qq_39885372/article/details/104080561