作者: 华丞臧.
专栏:【网络】
各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞+收藏+关注
)。如果有错误的地方,欢迎在评论区指出。
推荐一款刷题网站 LeetCode刷题网站
所以对于需要传输结构化数据,我们可以进行约定:
在进行结构化数据的网络通信时,需要将数据序列化成字符串再发送给对方,但是对方并不知道传输数据的长度,也不知道如何从字符串的数据中读取结构化的数据;因此在进行网络通信之间,通信的双发需要进行约定,比如:约定如何确定序列化数据的长度以及用什么格式来反序列化字符串。
在这里我们先手写一个定制协议,约定如下:
\r\n
隔开;\r\n
隔开#pragma once
#include
#include
#include
#include "Log.hpp"
#define CRLF "\r\n"
#define CRLF_LEN strlen(CRLF)
#define SPACE " "
#define SPACE_LEN strlen(SPACE)
#define OPS "+-*/%"
// encode,对整个序列化之后的字符串进行添加长度
//"strlen\r\nXXXXXX\r\n"
std::string encode(std::string &in, uint32_t len)
{}
// decode,整个序列化之后的字符串进行提取长度
// 1. 必须具有完整的长度
// 2. 必须具有和len相符合的有效载荷
// 我们才返回有效载荷和len
// 否则,我们就是一个检测函数!
// 9\r\n100 + 200\r\n 9\r\n112 / 200\r\n
std::string decode(std::string &in, uint32_t *len)
{}
// 定制请求
class Request
{
public:
Request()
{}
~Request()
{}
int& getX()
{ return x_;}
int& getY()
{ return y_;}
char& getOp()
{ return op_;}
// 序列化
void serialization(std::string *out)
{}
// 反序列化
bool deserialization(std::string &in)
{}
void debug()
{}
private:
// 需要计算的数据
int x_;
int y_;
// 需要计算的种类,"+ - * / %"
char op_;
};
// 定制响应
class Response
{
public:
Response()
:result_(0)
,exitCode_(0)
{}
~Response()
{}
// 序列化
void serialization(std::string *out)
{}
// 反序列化
bool deserialization(std::string &in)
{}
public:
// 退出状态,0标识运算结果合法,非0标识运算结果是非法的
int exitCode_;
// 运算结果
int result_;
};
bool makeReuquest(const std::string &str, Request *req)
{}
按照约定需要在序列化之后的字符串首部加上有效载荷的长度并用\r\n
隔开,并且结尾也需要加上\r\n
。
// encode,对整个序列化之后的字符串进行添加长度
//"strlen\r\nXXXXXX\r\n"
std::string encode(std::string &in, uint32_t len)
{
// "exitCode_ result_"
// "len\r\n" "exitCode_ result_\r\n"
std::string encodeStr = std::to_string(len);
encodeStr += CRLF;
encodeStr += in;
encodeStr += CRLF;
//std::cout << "debug->encode-> " << encodeStr << std::endl;
return encodeStr;
}
// decode,整个序列化之后的字符串进行提取长度
// 1. 必须具有完整的长度
// 2. 必须具有和len相符合的有效载荷
// 我们才返回有效载荷和len
// 否则,我们就是一个检测函数!
// 9\r\n100 + 200\r\n 9\r\n112 / 200\r\n
std::string decode(std::string &in, uint32_t *len)
{
assert(len);
// 1. 确认是否是一个包含len的有效字符串
*len = 0;
size_t pos = in.find(CRLF);
if(pos == std::string::npos)
return "";
// 2. 提取长度
std::string strLen = in.substr(0, pos);
int intLen = atoi(strLen.c_str());
// 3. 确认有效载荷也是符合要求的
int surplus = in.size() - 2 * CRLF_LEN - pos;
if(surplus < intLen)
{
return "";
}
// 4. 确认有完整的报文结构
std::string package = in.substr(pos + CRLF_LEN, intLen);
*len = intLen;
// 5. 将当前报文完整的从in中全部移除掉
int remLen = strLen.size() + 2 *CRLF_LEN + package.size();
in.erase(0, remLen);
// 6. 正常返回
return package;
}
定制请求,就是给请求方一个存放结构化数据的空间,请求方可以通过定制好的协议进行序列化得到字符串,然后就可以与服务端进行网络通信了;Request中的反序列化通常是给服务端提取请求方的结构化数据,所以服务端可以根据结构化数据向请求方进行响应。
序列化:
反序列化:
#define CRLF "\r\n"
#define CRLF_LEN strlen(CRLF)
#define SPACE " "
#define SPACE_LEN strlen(SPACE)
// 定制请求
class Request
{
public:
Request()
{}
~Request()
{}
int& getX()
{ return x_;}
int& getY()
{ return y_;}
char& getOp()
{ return op_;}
// 序列化
void serialization(std::string *out)
{
// 100 + 200
// "100 + 200"
std::string dateOne = std::to_string(x_);
std::string dateTwo = std::to_string(y_);
std::string dateOp = std::to_string(op_);
*out = dateOne;
*out += SPACE;
*out += op_;
*out += SPACE;
*out += dateTwo;
//std::cout << *out << std::endl;
}
// 反序列化
bool deserialization(std::string &in)
{
//std::cout << 0 << std::endl;
// 100 + 200
size_t spaceOne = in.find(SPACE);
if(spaceOne == std::string::npos) return false;
//std::cout << 1 << std::endl;
size_t spaceTwo = in.rfind(SPACE);
if(spaceTwo == std::string::npos) return false;
//std::cout << 2 << std::endl;
std::string dateOne = in.substr(0, spaceOne);
std::string dateTwo = in.substr(spaceTwo + SPACE_LEN);
std::string dateOp = in.substr(spaceOne + SPACE_LEN, spaceTwo - (spaceOne + SPACE_LEN));
if(dateOp.size() != 1)
{
printf("%d:%c\n", dateOp.size(), dateOp.c_str());
return false;
}
//std::cout << 3 << std::endl;
// 转成内部成员
x_ = atoi(dateOne.c_str());
y_ = atoi(dateTwo.c_str());
op_ = atoi(dateOp.c_str());
return true;
}
void debug()
{
std::cout << "x_" << x_ << std::endl;
std::cout << "y_" << y_ << std::endl;
std::cout << "op_" << op_ << std::endl;
}
private:
// 需要计算的数据
int x_;
int y_;
// 需要计算的种类,"+ - * / %"
char op_;
};
响应是服务端在对客户端的请求提供服务之后给客户端返回的结果,所以Response需要能够存放服务结果以及发生错误时的状态码;而结果和错误码是数据化结构,所以需要序列化之后在传输给客户端,客户端在拿到响应后需要进行反序列化拿到结果和状态码。序列胡和反序列化过程与定制请求类似。
#define CRLF "\r\n"
#define CRLF_LEN strlen(CRLF)
#define SPACE " "
#define SPACE_LEN strlen(SPACE)
// 定制响应
class Response
{
public:
Response()
:result_(0)
,exitCode_(0)
{}
~Response()
{}
// 序列化
void serialization(std::string *out)
{
// "exitCode_ result_"
std::string ec = std::to_string(exitCode_);
std::string res = std::to_string(result_);
*out = ec;
*out += SPACE;
*out += res;
}
// 反序列化
bool deserialization(std::string &in)
{
// "nextiCode_ result_"
size_t pos = in.find(SPACE);
if(pos == std::string::npos) return false;
std::string ec = in.substr(0, pos);
std::string res = in.substr(pos + SPACE_LEN);
exitCode_ = atoi(ec.c_str());
result_ = atoi(res.c_str());
return true;
}
public:
// 退出状态,0标识运算结果合法,非0标识运算结果是非法的
int exitCode_;
// 运算结果
int result_;
};
我们在客户端输入时是按照字符串的格式输入的,所以为了能够使用定制协议,需要进行反序列化即将输入的字符串数据按照协议中的结构化数据提取出来。
#define OPS "+-*/%"
bool makeReuquest(const std::string &str, Request *req)
{
// 123+1 1*1 1/1
char strtmp[1024];
snprintf(strtmp, sizeof strtmp, "%s", str.c_str());
char *left = strtok(strtmp, OPS);
if (!left)
return false;
char *right = strtok(nullptr, OPS);
if (!right)
return false;
char mid = str[strlen(left)];
req->getX() = atoi(left);
req->getY() = atoi(right);
req->getOp() = mid;
return true;
}
大佬也写了第三方库来支持序列化和反序列化,在这里我使用的jsoncpp,安装方式如下:
sudo yum install -y jsoncpp-devel
第三方库的使用较之我们自己实现无疑方便了很多,不用自己造轮子。
#pragma once
#include
#include
#include
#include
#include "Log.hpp"
#define CRLF "\r\n"
#define CRLF_LEN strlen(CRLF)
#define SPACE " "
#define SPACE_LEN strlen(SPACE)
#define OPS "+-*/%"
// 定制请求
class Request
{
public:
Request()
{}
~Request()
{}
int& getX()
{ return x_;}
int& getY()
{ return y_;}
char& getOp()
{ return op_;}
// 序列化
void serialization(std::string *out)
{
//json
// 1. Value对象,万能对象
// 2. json是基于KV
// 3. json有两套操作方法
// 4. 序列化的时候,会将所有的数据内容,转换成为字符串
Json::Value root; //,万能对象,任意类型
root["x"] = x_;
root["y"] = y_;
root["op"] = op_;
Json::FastWriter fw;
//Json::StyledWriter fw;
*out = fw.write(root); //序列化
}
// 反序列化
bool deserialization(std::string &in)
{
Json::Value root;
Json::Reader rd;
rd.parse(in, root);
x_ = root["x"].asInt();
y_ = root["y"].asInt();
op_ = root["op"].asInt();
return true;
}
void debug()
{
std::cout << "x_" << x_ << std::endl;
std::cout << "y_" << y_ << std::endl;
std::cout << "op_" << op_ << std::endl;
}
private:
// 需要计算的数据
int x_;
int y_;
// 需要计算的种类,"+ - * / %"
char op_;
};
// 定制响应
class Response
{
public:
Response()
:result_(0)
,exitCode_(0)
{}
~Response()
{}
// 序列化
void serialization(std::string *out)
{
Json::Value root;
root["exitCode"] = exitCode_;
root["result"] = result_;
Json::FastWriter fw;
*out = fw.write(root); //序列化
}
// 反序列化
bool deserialization(std::string &in)
{
Json::Value root;
Json::Reader rd;
rd.parse(in, root);
exitCode_ = root["exitCode"].asInt();
result_ = root["result"].asInt();
return true;
}
public:
// 退出状态,0标识运算结果合法,非0标识运算结果是非法的
int exitCode_;
// 运算结果
int result_;
};