应用层(Application layer)是OSI模型的第七层。应用层直接和应用程序接口并提供常见的网络应用服务。应用层也向表示层发出请求。应用层是开放系统的最高层,是直接为应用进程提供服务的。其作用是在实现多个系统应用进程相互通信的同时,完成一系列业务处理所需的服务。我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层。
运输层为应用进程提供了端到端的通信服务,但不同的网络应用的应用进程之间,还需要有不同的通信规则,因此在运输层协议之上,还需要有应用层协议。协议作为一种”约定“,那么必须遵守一些准则。对应应用层协议一般要遵守:
官方定义:序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
序列化有两个用途:
实际上就是将数据持久化,防止一直存储在内存当中,消耗内存资源。而且序列化后也能更好的便于网络运输何传播。
例如在微信里你給对方发送一条消息,实际上会将头像、昵称、消息内容、发送时间等构建一个结构,然后将该结构进行序列化,即将该结构形成一个字节流报文,通过网络将该报文发送給对方,对方进行反序列化,将该报文转化为结构,然后重新拆解为头像、昵称、消息内容、发送时间等。
而序列化还解决了网络传输结构体由于大小端、内存对齐导致数据出错等问题
现通过指定简单的协议,实现一个服务器版的计算器。我们需要在客户端把要计算的两个数和运算符发过去, 然后由服务器进行计算, 最 后再把结果返回给客户端。
对应网络计算器约定协议:
对应网络的序列化和反序列化:
在TCP协议中,如何保证接收方收到的是完整的报文呢?
我们调用的发送或接收函数,本质上是拷贝函数。
应用层调用的发送或接收函数,并不是直接从网络中读取或发送数据。例如客户端在应用层调用发送函数发送数据时,是将数据从应用层的发送缓冲区拷贝一份到传输层的发送缓冲区。然后由传输层自主决定何时将数据发送至网络中,服务器的传输层再通过网络将数据读取到接收缓冲区,然后将数据拷贝一份到应用层的接收缓冲区。因此TCP协议是一种传输控制协议
由于使用在TCP协议的双方都有发送缓冲区和接收缓冲区,即读取数据和发送数据不会互相干扰,可以同时双向传输数据,因此TCP协议是一种全双工的通信协议。
由于TCP协议是一种全双工的通信协议,因此也会产生客户端发送数据的速度远远大于服务器读取数据的速度,此时会造成服务器的接收缓冲区内积攒大量的报文,这些报文是线性连接在一起的,那么如何将一条条报文完整的读取上来呢?**通过指定的协议,按照协议规定的方式读取上来。**协议定制的方式有:
- 定长:规定该报文的长度
- 间隔符号:规定报文之间存在间隔符号
- 自描述方式:自定义协议
该网络版计算器对应的协议如下:
protocol.hpp
#pragma once
#include
#include
#include
#include
#include
using namespace std;
#define SEP " "
#define SEP_LEN strlen(SEP)//strlen统计'\0'之前的字符个数,而sizeof统计的是所占内存的空间大小,使用sizeof会越界出问题
#define LINE_SEP "\r\n"
#define LINE_SEP_LEN strlen(LINE_SEP)
enum {
NONE=0,
DIV_ZERO,
MOD_ZERO,
OP_ERR
};
//"x op y"->"text_len"\r\n"x op y"\r\n---給内容加上报头
std::string enLength(const std::string& text)//协议定制
{
std::string send_str=to_string(text.size());
send_str+=LINE_SEP;
send_str+=text;
send_str+=LINE_SEP;
return send_str;
}
//"text_len"\r\n"x op y"\r\n -> "x op y"---去掉报头,取出里面的内容
bool deLength(const std::string& str,string* ret)//协议定制
{
auto it=str.find(LINE_SEP);//找到报头
if(it==std::string::npos) return false;//如果没找到则直接返回
int len=stoi(str.substr(0,it));//取出字符串的长度
*ret=str.substr(it+LINE_SEP_LEN,len);//取出数据
return true;
}
class Request
{
public:
Request():_x(0),_y(0),_op(0){}
Request(int x,int y,int op):_x(x),_y(y),_op(op){}
bool Serialize(std::string* out)//序列化,将传入的x op y转化为字符串"x op y"
{
*out="";
*out+=to_string(_x);
*out+=SEP;
*out+=to_string(_op);
*out+=SEP;
*out+=to_string(_y);
return true;
}
bool Deserialize( const string& origin)//反序列化,将传过来的字符串拆出来传参給_x _op _y
{//"_xSEP_opSEP_y"-> _x,_op,_y
auto leftit=origin.find(SEP);
cout<<"Deserialize找到了leftSEP: "<<leftit<<endl;
auto rightit=origin.rfind(SEP);
cout<<"Deserialize找到了rightSEP: "<<rightit<<endl;
if(leftit==string::npos|| rightit==string::npos) return false;
if(leftit==rightit) return false;
int opsize=rightit-leftit-1;
cout<<"opsize: "<<opsize<<endl;
//1 43 1--leftit=1,rightit=4,opsize=rightit-leftit-1=4-1-1=2;
//1 3 1--leftit=1,right=3,opsize=rightit-leftit-1=3-1-1=1
// if(rightit-(leftit+SEP_LEN)!=1) return false;
if(rightit-(leftit+SEP_LEN)!=opsize) return false;
//+号ASCII码是43,从char转int被解析成43即stringlen为两位,这里的运算rightit-(leftit+SEP_LEN)!=1就出问题
//4-(1+1)==2;3-(1+1)=1
std::string origin_x=origin.substr(0,leftit);
std::string origin_y=origin.substr(rightit+SEP_LEN);
if(origin_x.empty()) return false;
if(origin_y.empty()) return false;
cout<<"origin_x: "<<origin_x<<" origin_y: "<<origin_y<<endl;
_x=stoi(origin_x);
int opf=stoi(origin.substr(leftit,rightit));
_op=opf;
cout<<"opf: "<<opf<<"_op: "<<_op<<endl;
_y=stoi(origin_y);
return true;
}
public:
int _x;
int _y;
char _op;
};
class Response
{
public:
Response():_exitcode(0),_result(0){}
Response(int exitcode,int result):_exitcode(exitcode),_result(result){}
bool Serialize(string*out)//序列化
{//_exitcode _result ->"_exitcodeSEP_result"
*out="";
*out+=to_string(_exitcode);
*out+=SEP;
*out+=to_string(_result);
return true;
}
bool Deserialize(const string& in)//反序列化
{//_exitcodeSEP_result"->_exitcode _result
auto pos=in.find(SEP);
if(pos==string::npos) return false;
string excstr=in.substr(0,pos);
string resstr=in.substr(pos+SEP_LEN);
if(excstr.empty()||resstr.empty()) return false;
_exitcode=stoi(excstr);
_result=stoi(resstr);
return true;
}
public:
int _exitcode;//退出码
int _result;//结果
};
//"text_len"\r\n"x op y"\r\n
bool recvPackage(int sock,string& inbuffer,string*out)
{
char buffer[1024];
while(true)
{
ssize_t s=recv(sock,buffer,sizeof(buffer)-1,0);
if(s>0)
{
buffer[s]=0;
inbuffer+=buffer;
auto pos=inbuffer.find(LINE_SEP);
if(pos==string::npos)continue;//没找到报头和有效载荷之间的分隔符---如果字节流式的报文没读全就继续读
string text_len=inbuffer.substr(0,pos);//报头是有效载荷的长度
int len=stoi(text_len);
int totallen=text_len.size()+LINE_SEP_LEN*2+len;//整个报文的长度
if(inbuffer.size()<totallen)
{
cout<<"输入的消息不完整,请继续输入.continue..."<<endl;
continue;//报文没读完继续读
}
cout<<"处理前的inbuffer: \n"<<inbuffer<<endl;
*out=inbuffer.substr(0,totallen);
inbuffer.erase(0,totallen);
cout<<"处理后的inbuffer: \n"<<inbuffer<<endl;
break;
}
else return false;
}
return true;
}
介绍一下:
enLength协议定制函数,給传入的字符串加上"报头",传入字符串"xSEPopSEPy",加上"报头后字符串为"text_len"\r\n"xSEPopSEPy"\r\n。其中"xSEPopSEPy"为有效载荷,text_len为有效载荷的长度,SEP是空格。
deLength协议定制函数。函数作用是給传入的字符串str去"报头",并把有效载荷通过ret传出去。传入的字符串是"text_len"\r\n"xSEPopSEPy"\r\n,去掉"报头"后,取出字符串 “xSEPopSEPy”。其中SEP是空格。
recvPackage函数是供服务器调用接收数据包的函数。服务器接收客户端发送来的报文,若报文不符合定义的协议形式或者没读上来的报文不是完整的则阻塞式读取,直到读上来完整的报文。然后通过输出型参数out将报文传出去。
calserver.cc
#include"calserver.hpp"
#include"log.hpp"
#include
#include
#include
using namespace Server;
using namespace std;
static void Usage(string proc)
{
cout<<"\nUsage:\n\t"<<proc<<" local_port\n\n"<<endl;
}
//req是一个已经处理的完整的对象
bool cal(const Request& req,Response& rep)
//根据req填充rep
{
switch(req._op)
{
case '+':
rep._result=req._x+req._y;
break;
case '-':
rep._result=req._x-req._y;
break;
case '*':
rep._result=req._x*req._y;
break;
case '/':
{
if(req._y==0) rep._exitcode=DIV_ZERO;
else
rep._result=req._x/req._y;
}
break;
case '%':
{
if(req._y==0) rep._exitcode=MOD_ZERO;
else
rep._result=req._x%req._y;
}
break;
default:
rep._exitcode=OP_ERR;
break;
}
return true;
}
int main(int argc,char* argv[])
{
if(argc!=2)
{
Usage(argv[0]);
exit(USAGE_ERR);
}
uint16_t port=atoi(argv[1]);//将字符串转化为整数
unique_ptr<calserver> ts(new calserver(port));
ts->initserver();
ts->start(cal);
return 0;
}
calserver.hpp
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include"log.hpp"
#include"protocol.hpp"
#define NUM 1024
using namespace std;
static const int gbacklog = 5;
namespace Server
{
enum
{
USAGE_ERR=1,SOCK_ERR,BIND_ERR,LISTEN_ERR
};
typedef function<bool(const Request&req,Response& res)> func_t;
void handlerentry(int sock,func_t func)
{
string inbuffer;
while(true)
{
//1. 获取客户端发送来的数据报,确定数据报是带报头的数据报
//"text_len"\r\n"x op y"\r\n
string req_text,req_str;
if(!recvPackage(sock,inbuffer,&req_text)) return;
cout<<"带报头的请求(数据报): "<<req_text<<endl;
//2.对数据报进行反序列化
//"text_len"\r\n"x op y"\r\n -> "x op y"---去掉报头,取出里面的内容
if(!deLength(req_text,&req_str)) return;
cout<<"去掉报头的请求(数据报):"<<req_str<<endl;
//走到这里再往下就卡主了,只打印到上面那条日志后面都没打印到!!!
//3.获得一个结构化的请求对象
Request req;
if(!req.Deserialize(req_str)) return;//如果反序列化失败直接返回
//4.对对象进行操作---进行服务器业务
//4.1.获得一个结构化响应
Response rep;
func(req,rep);
//5.对对象进行序列化
//_exitcode _result ->"_exitcodeSEP_result"
string rep_str;
rep.Serialize(&rep_str);
cout<<"计算完成后的响应: "<<rep_str<<endl;
//6.給有效载荷加上报头
//"exitcode result" -> "content_len"\r\n"exitcode result"\r\n
string rep_text=enLength(rep_str);
cout<<"加上报头的完整响应(报文): "<<rep_text<<endl;
//7.把报文发送回給客户端
send(sock,rep_text.c_str(),rep_text.size(),0);
}
}
typedef function<bool(const Request&req,Response& res)> func_t;
class calserver
{
public:
calserver(const uint16_t& port):_port(port),_listensock(-1){}
void initserver()
{
//1.创建套接字
_listensock=socket(AF_INET,SOCK_STREAM,0);
if(_listensock<0)
{
logMessage(FATAL,"create listensocket error");
exit(SOCK_ERR);
}
logMessage(NORMAL, "create socket success: %d", _listensock);
//2.bind ip和port
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(_port);
local.sin_addr.s_addr=INADDR_ANY;
if(bind(_listensock,(struct sockaddr*)&local,sizeof(local))<0)//绑定失败
{
logMessage(FATAL,"bind error");
exit(BIND_ERR);
}
logMessage(NORMAL,"bind success");
//3.将套接字设置为监听模式
if(listen(_listensock,gbacklog)<0)
{
logMessage(FATAL,"listen error");
exit(LISTEN_ERR);
}
logMessage(NORMAL,"listen success");
}
void start(func_t fun)
{
while(true)
{
struct sockaddr_in cli;
socklen_t len=sizeof(cli);
bzero(&cli,len);
int sock=accept(_listensock,(struct sockaddr*)&cli,&len);
if(sock<0)
{
logMessage(FATAL,"accept client error");
continue;
}
logMessage(NORMAL,"accept client success");
cout<<"accept sock: "<<sock<<endl;
//多进程版---
//一个客户端占用一个文件描述符,原因在于孙子进程执行IO任务需要占用独立的文件描述符,而文件描述符是继承父进程的,而每次客户端进来都要占用新的文件描述符
//因此若接收多个客户端不退出的话文件描述符会越来越少。
pid_t id=fork();//创建子进程
if(id==0)//子进程进入
{
close(_listensock);//子进程不需要用于监听因此关闭该文件描述符
handlerentry(sock,fun);
close(sock);
exit(0);
}
//父进程
close(sock);
pid_t ret=waitpid(id,nullptr,0);
if(ret<0)
{
cout << "waitsuccess: " << ret << endl;
}
}
}
~calserver(){}
private:
int _listensock;//用于监听服务器的sock文件描述符
uint16_t _port;//端口号
};
}
calclient.cc
#include
#include
#include
#include"calclient.hpp"
using namespace std;
using namespace client;
static void Usage(string proc)
{
cout<<"\nUsage :\n\t"<<proc<<" serverip serverport\n"<<endl;
}
int main(int argc, char* argv[])
{
if(argc!=3)
{
Usage(argv[0]);
exit(1);
}
string serverip=argv[1];
uint16_t serverport=atoi(argv[2]);
unique_ptr<calclient> tc(new calclient(serverip,serverport));
tc->initclient();
tc->start();
return 0;
}
calclient.hpp
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include"protocol.hpp"
using namespace std;
#define NUM 1024
namespace client
{
class calclient
{
public:
calclient(const string& ip,const uint16_t& port)
:_sock(-1)
,_port(port)
,_ip(ip)
{}
void initclient()
{
//1.创建sockfd
_sock=socket(AF_INET,SOCK_STREAM,0);
if(_sock<0)
{
cerr<<"socket create error"<<endl;
exit(2);
}
//2.绑定 ip port,不显示绑定,OS自动绑定
}
void start()
{
struct sockaddr_in ser;
bzero(&ser,sizeof(ser));
socklen_t len=sizeof(ser);
ser.sin_family=AF_INET;
ser.sin_port=htons(_port);
ser.sin_addr.s_addr=inet_addr(_ip.c_str());
if(connect(_sock,(struct sockaddr *)&ser,len)!=0)
{
cerr<<"connect error"<<endl;
}else
{
string line;
string inbuffer;
while(true)
{
cout<<"mycal>>: ";//输入"xopy"
getline(cin,line);
Request req=ParseLine(line);//用"xopy"取出x op y构造Request对象
string context;
req.Serialize(&context);//序列化,用x op y构造字符串"xSEPopSEPy"
string send_str=enLength(context);//定制协议---"x op y"->"text_len"\r\n"x op y"\r\n---給内容加上报头
cout<<"calclient send str: "<<send_str<<endl;
send(_sock,send_str.c_str(),send_str.size(),0);//客户端把报文发送給服务器
string package;
if(!recvPackage(_sock,inbuffer,&package)) continue;//服务器处理完数据,客户端接收服务器发送来的报文
// "content_len"\r\n"exitcode result"\r\n
string reser_len;
if(!deLength(package,&reser_len)) continue;//去报头
// "content_len"\r\n"exitcode result"\r\n -> "exitcode result"
Response rep;
rep.Deserialize(reser_len);//反序列化://_exitcodeSEP_result"->_exitcode _result
cout<<"_exitcode: "<<rep._exitcode<<endl;
cout<<"_result: "<<rep._result<<endl;
}
}
}
~calclient()
{
if(_sock>=0) close(_sock);
}
Request ParseLine(const string& line)
{//"xopy"->取出来到x op y 上
int i=0;
int status=0;
int num=line.size();
string left,right;
char op;
while(i<num)
{
switch(status)
{
case 0:
{
if(!isdigit(line[i]))
{
op=line[i];//取出运算符**
status=1;
}else
left.push_back(line[i++]);//取出左操作数
}
break;
case 1:
i++;
status=2;
break;
case 2:
right.push_back(line[i++]);
break;
}
}
cout<<"left: "<<stoi(left)<<" op: "<<op<<" right: "<<stoi(right)<<endl;
return Request(stoi(left),stoi(right),op);//返回Request对象
}
private:
int _sock;
uint16_t _port;
string _ip;
};
}
makefile
.PHONY:all
all:calclient calserver
calclient:calclient.cc
g++ -o $@ $^ -std=c++11
calserver:calserver.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -rf calserver calclient
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它以易于阅读和写作的文本形式表示结构化数据。JSON由两种结构构成:键值对(键值对集合)和值的有序列表。在这里以键值对的方式使用。
输入以下指令安装
sudo yum install -y jsoncpp-devel
安装完后可通过ls查询
makefile
cc=g++
LD=-DMYPRO
.PHONY:all
all:calclient calserver
calclient:calclient.cc
$(cc) -o $@ $^ -std=c++11 -ljsoncpp #${LD}
calserver:calserver.cc
$(cc) -o $@ $^ -std=c++11 -ljsoncpp #${LD}
.PHONY:clean
clean:
rm -rf calserver calclient
LD=-DMYPRO
protocol.hpp
#pragma once
#include
#include
#include
#include
#include
#include
using namespace std;
#define SEP " "
#define SEP_LEN strlen(SEP)//strlen统计'\0'之前的字符个数,而sizeof统计的是所占内存的空间大小,使用sizeof会越界出问题
#define LINE_SEP "\r\n"
#define LINE_SEP_LEN strlen(LINE_SEP)
enum {
NONE=0,
DIV_ZERO,
MOD_ZERO,
OP_ERR
};
//"x op y"->"text_len"\r\n"x op y"\r\n---給内容加上报头
std::string enLength(const std::string& text)//协议定制
{
std::string send_str=to_string(text.size());
send_str+=LINE_SEP;
send_str+=text;
send_str+=LINE_SEP;
return send_str;
}
//"text_len"\r\n"x op y"\r\n -> "x op y"---去掉报头,取出里面的内容
bool deLength(const std::string& str,string* ret)//协议定制
{
auto it=str.find(LINE_SEP);//找到报头
if(it==std::string::npos) return false;//如果没找到则直接返回
int len=stoi(str.substr(0,it));//取出字符串的长度
*ret=str.substr(it+LINE_SEP_LEN,len);//取出数据
return true;
}
class Request
{
public:
Request():_x(0),_y(0),_op(0){}
Request(int x,int y,int op):_x(x),_y(y),_op(op){}
bool Serialize(std::string* out)//序列化,将传入的x op y转化为字符串"x op y"
{
#ifdef MYPRO
*out="";
*out+=to_string(_x);
*out+=SEP;
*out+=to_string(_op);
*out+=SEP;
*out+=to_string(_y);
#else
Json::Value root;//json的对象是键值对[key,value]
root["first"]=_x;//int类型被设置进json的键值对时自动转换为string类型
root["second"]=_y;
root["oper"]=_op;
Json::FastWriter writer;
*out=writer.write(root);//调用接口序列化返回值为字符串
#endif
return true;
}
bool Deserialize( const string& origin)//反序列化,将传过来的字符串拆出来传参給_x _op _y
{//"_xSEP_opSEP_y"-> _x,_op,_y
#ifdef MYPRO
auto leftit=origin.find(SEP);
cout<<"Deserialize找到了leftSEP: "<<leftit<<endl;
auto rightit=origin.rfind(SEP);
cout<<"Deserialize找到了rightSEP: "<<rightit<<endl;
if(leftit==string::npos|| rightit==string::npos) return false;
if(leftit==rightit) return false;
int opsize=rightit-leftit-1;
cout<<"opsize: "<<opsize<<endl;
//1 43 1--leftit=1,rightit=4,opsize=rightit-leftit-1=4-1-1=2;
//1 3 1--leftit=1,right=3,opsize=rightit-leftit-1=3-1-1=1
// if(rightit-(leftit+SEP_LEN)!=1) return false;
if(rightit-(leftit+SEP_LEN)!=opsize) return false;
//+号ASCII码是43,从char转int被解析成43即stringlen为两位,这里的运算rightit-(leftit+SEP_LEN)!=1就出问题
//4-(1+1)==2;3-(1+1)=1
std::string origin_x=origin.substr(0,leftit);
std::string origin_y=origin.substr(rightit+SEP_LEN);
if(origin_x.empty()) return false;
if(origin_y.empty()) return false;
cout<<"origin_x: "<<origin_x<<" origin_y: "<<origin_y<<endl;
_x=stoi(origin_x);
int opf=stoi(origin.substr(leftit,rightit));
_op=opf;
cout<<"opf: "<<opf<<"_op: "<<_op<<endl;
_y=stoi(origin_y);
#else
Json::Value root;
Json::Reader reader;
reader.parse(origin,root);//反序列化,将字符串中的协议字符串填进对象对应的元素中
_x=root["first"].asInt();
_y=root["second"].asInt();
_op=root["oper"].asInt();
#endif
return true;
}
public:
int _x;
int _y;
char _op;
};
class Response
{
public:
Response():_exitcode(0),_result(0){}
Response(int exitcode,int result):_exitcode(exitcode),_result(result){}
bool Serialize(string*out)//序列化
{//_exitcode _result ->"_exitcodeSEP_result"
#ifdef MYPRO
*out="";
*out+=to_string(_exitcode);
*out+=SEP;
*out+=to_string(_result);
#else
Json::Value root;
root["exitcode"]=_exitcode;
root["result"]=_result;
Json::FastWriter writer;
*out= writer.write(root);
#endif
return true;
}
bool Deserialize(const string& in)//反序列化
{//_exitcodeSEP_result"->_exitcode _result
#ifdef MYPRO
auto pos=in.find(SEP);
if(pos==string::npos) return false;
string excstr=in.substr(0,pos);
string resstr=in.substr(pos+SEP_LEN);
if(excstr.empty()||resstr.empty()) return false;
_exitcode=stoi(excstr);
_result=stoi(resstr);
#else
Json::Value root;
Json::Reader reader;
reader.parse(in,root);
_exitcode=root["exitcode"].asInt();
_result=root["result"].asInt();
#endif
return true;
}
public:
int _exitcode;//退出码
int _result;//结果
};
//"text_len"\r\n"x op y"\r\n
bool recvPackage(int sock,string& inbuffer,string*out)
{
char buffer[1024];
while(true)
{
ssize_t s=recv(sock,buffer,sizeof(buffer)-1,0);
if(s>0)
{
buffer[s]=0;
inbuffer+=buffer;
auto pos=inbuffer.find(LINE_SEP);
if(pos==string::npos)continue;//没找到报头和有效载荷之间的分隔符---如果字节流式的报文没读全就继续读
string text_len=inbuffer.substr(0,pos);//报头是有效载荷的长度
int len=stoi(text_len);
int totallen=text_len.size()+LINE_SEP_LEN*2+len;//整个报文的长度
if(inbuffer.size()<totallen)
{
cout<<"输入的消息不完整,请继续输入.continue..."<<endl;
continue;//报文没读完继续读
}
cout<<"处理前的inbuffer: \n"<<inbuffer<<endl;
*out=inbuffer.substr(0,totallen);
inbuffer.erase(0,totallen);
cout<<"处理后的inbuffer: \n"<<inbuffer<<endl;
break;
}
else return false;
}
return true;
}
格式
#ifdef DEBUG
//......
#else
//......
#endif
//Request类内的Json序列化片段-Serialize
Json::Value root;//json的对象是键值对[key,value]
root["first"]=_x;//int类型被设置进json的键值对时自动转换为string类型
root["second"]=_y;
root["oper"]=_op;
Json::FastWriter writer;
*out=writer.write(root);//调用接口序列化返回值为字符串
Request类内的Json反序列化片段-Deserialize
Json::Value root;
Json::Reader reader;
reader.parse(origin,root);//反序列化,将字符串中的协议字符串填进对象对应的元素中
_x=root["first"].asInt();
_y=root["second"].asInt();
_op=root["oper"].asInt();