Linux系统编程:自定义协议(序列化和反序列化)

1. 协议

        在之前我们谈到,协议就是一种"约定",socket api接口,在读写数据时,都是按照"字符串"的方式来发送接收的,那么我们要传输一些"结构化"数据时怎么办呢?,比如说一个结构体 eg:

struct message{
    string url;
    string time;
    string id;
    string msg;
};

我们可以将数据,变为一个字符串(有效载荷),并为其添加报头(包含数据的一些属性),最后形成一个报文,这个过程就是序列化的过程 再将这个报文发送到网络中;另一台主机从网络中接收到该数据,将其取报头,并且将字符串转换为我们上面的结构化数据,这个过程就是反序列化。

业务结构化数据在发送到玩过中时先序列化再发送,收到的数据一定是序列字节流,要先进行反序列化,然后才能使用。

2. 自定义协议

下面我们实现一个网络版本的服务端和客户端,你并且自定义一个协议,实现序列化反序列化的过程。

makefile文件:

cc=g++
LD=-DMYSELF
.PHONY:all
all:calServer calClient

calServer:calServer.cc
	$(cc) -o $@ $^ -std=c++11 -ljsoncpp ${LD}
calClient:calClient.cc
	$(cc) -o $@ $^ -std=c++11 -ljsoncpp ${LD}

.PHONY:clean
clean:
	rm -f calClient calServer

log.hpp(日志):

#pragma once

#include <iostream>
#include <cstring>
#include <string>
#include <stdarg.h>
#include <ctime>
#include <unistd.h>

using namespace std;

#define DEBUG   0
#define NORMAL  1
#define WARNING 2
#define ERROR   3  // 出错可运行
#define FATAL   4  // 致命错误

const char* to_levelStr(int level)
{
    switch ((level))
    {
    case DEBUG: return "DEBUG";
    case NORMAL: return "NORMAL";
    case WARNING: return "WARNING";
    case ERROR: return "ERROR";
    case FATAL: return "FATAL";
    default: return nullptr;
    }
}

void logMessage(int level, const char* format, ...) // ... 可变参数列表 
{
#define NUM 1024
    char logPreFix[NUM];
    snprintf(logPreFix, sizeof(logPreFix), "[%s][%ld][pid: %d]", to_levelStr(level), (long int)time(nullptr), getpid());

    char logContent[NUM];
    va_list arg;
    va_start(arg, format);

    vsnprintf(logContent, sizeof(logContent), format, arg);
    
    cout << logPreFix << logContent << endl;

   
}

 protocol.hpp:

在这个文件中,定义了请求和响应类,并在类中实现了请求和响应的序列化(serialize)和反序列化(deserialize)操作;并且实现了添加报头(enLength)和去报头的操作(deLength)

#pragma once

#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <sys/socket.h>
#include <sys/types.h>
#include <jsoncpp/json/json.h>

#define SEP " "
#define SEP_LEN strlen(SEP)  // 不能使用sizeof()
#define LINE_SEP "\r\n"
#define LINE_SEP_LEN strlen(LINE_SEP)

enum { OK = 0, DIV_ZERO, MOD_ZERO, OP_ERROR };  

// "_exitcode _result" -> "content_len"\r\n"_exitcode _result"\r\n
// "_x _op _y\r\n" -> "content_len"\r\n"__x _op _y"\r\n
std::string enLength(const std::string& text)
{
    std::string send_string = std::to_string(text.size());
    send_string += LINE_SEP;
    send_string += text; 
    send_string += LINE_SEP;

    return send_string;
}

// "content_len"\r\n"_x _op _y"\r\n -> "_x _op _y"
bool deLength(const std::string& package, std::string* text)
{
    auto pos = package.find(LINE_SEP);
    if(pos == std::string::npos) return false;
    std::string text_len_string = package.substr(0, pos);
    int text_len = std::stoi(text_len_string);
    *text = package.substr(pos+LINE_SEP_LEN, text_len);
    return true;
}

// 没有人规定我们的网络通信的时候 只能有一种协议
// 我们如何让系统知道我们用的哪一种协议?
// "协议编号"\r\n"content_len"\r\n"_exitcode _result"\r\n

class Request
{
public:
    Request()
        :_x(0)
        ,_y(0)
        ,_op(0)
    {}
    
    Request(int x, int y, char op)
        :_x(x)
        ,_y(y)
        ,_op(op)
    {}

    // 序列化 自己写、现成的
    bool serialize(std::string* out)
    {
        *out = "";
        // 结构化 -> "_x _op _y\r\n" 一个请求就一行
        std::string x_string = std::to_string(_x);
        std::string y_string = std::to_string(_y);
        *out = x_string;
        *out +=
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值