当前位置: 首页 > article >正文

自定义协议以及序列化和反序列化

我们知道TCP是全双工的,可以同时进行发收,因为他有一个发送缓冲区和一个接收缓冲区

我们使用write其实是把数据拷贝到发送缓冲区,使用read接收缓冲区的数据,其实是把数据拷贝到文件缓冲区里,发送的过程中,我们怎么保证发送的是完整的数据?接收的是完整的数据呢?

那就需要协议了,协议是种约定,我们双方客户端和服务器约定好的。socket api的接口, 在读写数据时, 都是按 "字符串" 的方式来发送接收的. 如果我们要传输一些"结构化的数据" 怎么办呢?我们一般是不发送结构体的数据的,因为在跨平台环境下,结构体的大小都会有所不同,所以我们一般把结构体的数据进行序列化,把它变成字符串,对方接收数据在进行反序列化变回结构体

序列化:结构体 ==> 字符串

反序列化:字符串 ==> 结构体

网络版本计数器

协议定制

例如, 我们需要实现一个服务器版的计算器. 我们需要客户端把要计算的两个数据发过去, 然后由服务器进行计算, 最后再把结果返回给客户端.

约定方案:

  • 定义结构体来表示我们需要交互的信息
  • 发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转化回结构体,这个过程叫做序列化和反序列化
  • 客户端发送序列化为"x + y"的正文字符串
  • 这个字符串中有两个操作数, 都是整形
  • 数字和运算符之间有空格
  • 添加报头和报尾来区分不同的正文字符串
  • “len”\n"x + y"\n,len表示正文字符串的长度,\n是为了分割和打印的
  • 客户端就这样“len”\n"x + y"\n发送数据
  • 服务器把计算结果和错误码发送回来,服务器发送序列化为"result code"的正文字符串
  • 同理添加报头和报尾,变成"len"\n"result code"的字符串

协议的实现

两种序列化反序列化的方法

  1. 手撕,一般不用,有一次就好了
  2. 利用json和protobuf,我们用json

介绍json:json是别人写的第三方库,你要用需要安装,它里面提供了序列化和反序列化的方法,它的使用也是很简单的,怎么用看下面代码的实现

Protocol.hpp

#pragma once
#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>

// #define MySelf 1

const std::string blank_space_sep = " ";
const std::string protocol_sep = "\n";

// 添加报头  "x + y" ==> "len"\n"x + y"\n
std::string Encode(const std::string &content)
{
    std::string package = std::to_string(content.size());
    package += protocol_sep;
    package += content;
    package += protocol_sep;
    return package;
}

// 去除报头  "len"\n"x + y"\n ==> "x + y"
bool Decode(std::string &package, std::string *content)
{
    size_t pos = package.find(protocol_sep);
    if (pos == std::string::npos)
        return false;
    std::string part_len = package.substr(0, pos);
    size_t len = std::stoi(part_len);
    std::size_t total_len = part_len.size() + len + 2;
    if (package.size() < total_len)
        return false;

    *content = package.substr(pos + 1, len);
    // earse 移除报文
    package.erase(0, total_len);
    return true;
}

class Request
{
public:
    Request(int data1, int data2, char oper) : x(data1), y(data2), op(oper)
    {
    }
    Request()
    {
    }
    ~Request()
    {
    }

public:
    bool Serialize(std::string *out)
    {
#ifdef MySelf // 自定义的
        // 构建报文的有效载荷
        // struct => string, "x op y"
        std::string s = std::to_string(x);
        s += blank_space_sep;
        s += op;
        s += blank_space_sep;
        s += std::to_string(y);
        *out = s;
        return true;
#else // 利用json
        Json::Value root;
        root["x"] = x;
        root["y"] = y;
        root["op"] = op;
        Json::StyledWriter w;  //显示一整个字符串
        // Json::FastWriter w;  //以结构体形式来显示
        *out = w.write(root);
        return true;
#endif
    }
    bool Deserialize(std::string &in)
    {
#ifdef MySelf
         //string, "x op y" => struct
        size_t left = in.find(blank_space_sep);
        if (left == std::string::npos)
            return false;
        std::string part_x = in.substr(0, left);

        size_t right = in.rfind(blank_space_sep);
        if (right == std::string::npos)
            return false;
        std::string part_y = in.substr(right + 1);
        if (left + 2 != right)
            return false;

        op = in[left + 1];
        x = std::stoi(part_x);
        y = std::stoi(part_y);
        return true;

#else
        Json::Value root;
        Json::Reader r;
        r.parse(in, root);
        x = root["x"].asInt();
        y = root["y"].asInt();
        op = root["op"].asInt();
        return true;
#endif
    }
    void DebugPrint()
    {
        std::cout << "新请求构建完成:  " << x << op << y << "=?" << std::endl;
    }

public:
    int x;
    int y;
    char op;
};

class Response
{
public:
    Response(int res, int c) : result(res), code(c)
    {
    }
    Response()
    {
    }
    ~Response()
    {
    }
    bool Serialize(std::string *out)
    {
#ifdef MySelf
        // 构建报文的有效载荷
        // struct => string, "result code"
        std::string s = std::to_string(result);
        s += blank_space_sep;
        s += std::to_string(code);
        *out = s;

        return true;
#else
        Json::Value root;
        root["result"] = result;
        root["code"] = code;
        Json::StyledWriter w;   //显示一整个字符串
        // Json::FastWriter w;  //以结构体形式来显示
        *out = w.write(root);
        return true;
#endif
    }
    bool Deserialize(std::string &in)
    {
#ifdef MySelf
       // string, "result code" => struct
        size_t pos = in.find(blank_space_sep);
        if (pos == std::string::npos)
            return false;
        std::string part_left = in.substr(0, pos);
        std::string part_right = in.substr(pos + 1);

        result = std::stoi(part_left);
        code = std::stoi(part_right);

        return true;

#else
        Json::Value root;
        Json::Reader r;
        r.parse(in, root);
        result = root["result"].asInt();
        code = root["code"].asInt();
        return true;
#endif
    }
    void DebugPrint()
    {
        std::cout << "结果响应完成, result: " << result << ", code: " << code << std::endl;
    }

public:
    int result;
    int code;
};

解释json的用法

下面是网络版计数器的服务器和客户端的全部代码

网络版服务器


http://www.kler.cn/news/337411.html

相关文章:

  • 基于Python的人工智能应用案例系列(16):LSTM能源生产预测
  • 社区圈子系统 圈子社区系统 兴趣社区圈子论坛系统 圈子系统源码圈子系统的适用领域有哪些?如何打造自己的圈子圈子系统有哪些常见问题
  • 在忘记密码的情况下重新访问手机?5种忘记密码解锁Android手机的方法
  • 系统架构设计师教程 第14章 14.1 云原生架构产生背景 笔记
  • 通信工程学习:什么是IOT物联网
  • 行为设计模式 -观察者模式- JAVA
  • 基于单片机的路灯控制系统的设计
  • SpringBoot企业级开发(SpringSecurity安全控制+pringBatch批处理+异步消息+系统集成SpringIntegration)
  • 【Ubuntu】git
  • HDLBits中文版,标准参考答案 |2.5 More Verilog Features | 更多Verilog 要点
  • 设计模式之原型模式(通俗易懂--代码辅助理解【Java版】)
  • 三、Python基础语法(注释、三种波浪线、变量)
  • 数据飞轮赋能科学决策:火山引擎 DataTester 升级 A/B 大模型评测
  • 【韩顺平Java笔记】第8章:面向对象编程(中级部分)【272-284】
  • 冯诺依曼体系结构与操作系统简介
  • 回归任务学习笔记
  • 自动驾驶 车道检测实用算法
  • 系统架构设计师-论文题(2018年下半年)
  • 【学习资源】人在环路的机器学习
  • ruoyi-python 若依python版本部署及新增模块【问题解决】