重庆建设机电有限公司网站,如何开通网络,食品包装设计案例分析,软装设计师培训模拟实现应用层协议 文章目录 模拟实现应用层协议应用层再谈协议 序列化和反序列化 网络版计算器自定义协议利用Json进行序列化和反序列化json库的安装条件编译 应用层 应用层#xff08;Application layer#xff09;是OSI模型的第七层。应用层直接和应用程序接口并提供常见…模拟实现应用层协议 文章目录 模拟实现应用层协议应用层再谈协议 序列化和反序列化 网络版计算器自定义协议利用Json进行序列化和反序列化json库的安装条件编译 应用层 应用层Application layer是OSI模型的第七层。应用层直接和应用程序接口并提供常见的网络应用服务。应用层也向表示层发出请求。应用层是开放系统的最高层,是直接为应用进程提供服务的。其作用是在实现多个系统应用进程相互通信的同时,完成一系列业务处理所需的服务。我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层。
再谈协议
运输层为应用进程提供了端到端的通信服务但不同的网络应用的应用进程之间还需要有不同的通信规则因此在运输层协议之上还需要有应用层协议。协议作为一种”约定“那么必须遵守一些准则。对应应用层协议一般要遵守
应用进程交换的报文类型如请求报文和响应报文。各种报文类型的语法如报文中的各个字段及其详细描述。字段的语义即包含在字段中的信息的含义。进程何时如何发送报文以及对报文进行响应的规则
序列化和反序列化
官方定义序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间对象将其当前状态写入到临时或持久性存储区。以后可以通过从存储区中读取或反序列化对象的状态重新创建该对象。
序列化有两个用途
把对象的字节序列永久保存到硬盘上通常存放在一个文件中序列化对象在网络上传送对象的字节序列网络传输对象
实际上就是将数据持久化防止一直存储在内存当中消耗内存资源。而且序列化后也能更好的便于网络运输何传播。 例如在微信里你給对方发送一条消息实际上会将头像、昵称、消息内容、发送时间等构建一个结构然后将该结构进行序列化即将该结构形成一个字节流报文通过网络将该报文发送給对方对方进行反序列化将该报文转化为结构然后重新拆解为头像、昵称、消息内容、发送时间等。
而序列化还解决了网络传输结构体由于大小端、内存对齐导致数据出错等问题
网络版计算器
现通过指定简单的协议实现一个服务器版的计算器。我们需要在客户端把要计算的两个数和运算符发过去, 然后由服务器进行计算, 最 后再把结果返回给客户端。
对应网络计算器约定协议
客户端发送一个形如11的字符串这个字符串中有两个操作数都是整形两个操作数只间有一个运算符操作数和运算符之间不能有空格
对应网络的序列化和反序列化
定义一个结构体来表示我们需要交互的信息发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转化回结构体。这个过程就称为序列化和反序列化 在TCP协议中如何保证接收方收到的是完整的报文呢 我们调用的发送或接收函数本质上是拷贝函数。 应用层调用的发送或接收函数并不是直接从网络中读取或发送数据。例如客户端在应用层调用发送函数发送数据时是将数据从应用层的发送缓冲区拷贝一份到传输层的发送缓冲区。然后由传输层自主决定何时将数据发送至网络中服务器的传输层再通过网络将数据读取到接收缓冲区然后将数据拷贝一份到应用层的接收缓冲区。因此TCP协议是一种传输控制协议 由于使用在TCP协议的双方都有发送缓冲区和接收缓冲区即读取数据和发送数据不会互相干扰可以同时双向传输数据因此TCP协议是一种全双工的通信协议。 由于TCP协议是一种全双工的通信协议因此也会产生客户端发送数据的速度远远大于服务器读取数据的速度此时会造成服务器的接收缓冲区内积攒大量的报文这些报文是线性连接在一起的那么如何将一条条报文完整的读取上来呢**通过指定的协议按照协议规定的方式读取上来。**协议定制的方式有 定长规定该报文的长度间隔符号规定报文之间存在间隔符号自描述方式自定义协议
该网络版计算器对应的协议如下 由于UDP协议发送和接收数据都是以数据报的形式在传输过程中数据是完整的因此并不需要通过序列和反序列化、对数据添加特殊内容的方式去界定报文边界而TCP协议发送和接收数据是以字节流的方式就必须使用相关协议手段去标识、保护报文。
自定义协议 protocol.hpp #pragma once
#includeiostream
#includestring
#include sys/types.h
#include sys/socket.h
#include cstring
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 {NONE0,DIV_ZERO,MOD_ZERO,OP_ERR
};
//x op y-text_len\r\nx op y\r\n---給内容加上报头
std::string enLength(const std::string text)//协议定制
{std::string send_strto_string(text.size());send_strLINE_SEP;send_strtext;send_strLINE_SEP;return send_str;
}
//text_len\r\nx op y\r\n - x op y---去掉报头取出里面的内容
bool deLength(const std::string str,string* ret)//协议定制
{auto itstr.find(LINE_SEP);//找到报头if(itstd::string::npos) return false;//如果没找到则直接返回int lenstoi(str.substr(0,it));//取出字符串的长度*retstr.substr(itLINE_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;*outto_string(_x);*outSEP;*outto_string(_op);*outSEP;*outto_string(_y);return true;
}bool Deserialize( const string origin)//反序列化将传过来的字符串拆出来传参給_x _op _y
{//_xSEP_opSEP_y- _x,_op,_yauto leftitorigin.find(SEP);coutDeserialize找到了leftSEP: leftitendl;auto rightitorigin.rfind(SEP);coutDeserialize找到了rightSEP: rightitendl;if(leftitstring::npos|| rightitstring::npos) return false;if(leftitrightit) return false;int opsizerightit-leftit-1;coutopsize: opsizeendl;
//1 43 1--leftit1,rightit4,opsizerightit-leftit-14-1-12;
//1 3 1--leftit1,right3,opsizerightit-leftit-13-1-11// if(rightit-(leftitSEP_LEN)!1) return false;if(rightit-(leftitSEP_LEN)!opsize) return false;//号ASCII码是43从char转int被解析成43即stringlen为两位,这里的运算rightit-(leftitSEP_LEN)!1就出问题
//4-(11)2;3-(11)1std::string origin_xorigin.substr(0,leftit);std::string origin_yorigin.substr(rightitSEP_LEN);if(origin_x.empty()) return false;if(origin_y.empty()) return false;coutorigin_x: origin_x origin_y: origin_yendl;_xstoi(origin_x);int opfstoi(origin.substr(leftit,rightit));_opopf;coutopf: opf_op: _opendl;_ystoi(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;
*outto_string(_exitcode);
*outSEP;
*outto_string(_result);return true;
}bool Deserialize(const string in)//反序列化
{//_exitcodeSEP_result-_exitcode _resultauto posin.find(SEP);
if(posstring::npos) return false;string excstrin.substr(0,pos);
string resstrin.substr(posSEP_LEN);
if(excstr.empty()||resstr.empty()) return false;_exitcodestoi(excstr);
_resultstoi(resstr);return true;}public:
int _exitcode;//退出码
int _result;//结果
};//text_len\r\nx op y\r\n
bool recvPackage(int sock,string inbuffer,string*out)
{
char buffer[1024];while(true)
{
ssize_t srecv(sock,buffer,sizeof(buffer)-1,0);
if(s0)
{buffer[s]0;inbufferbuffer;auto posinbuffer.find(LINE_SEP);if(posstring::npos)continue;//没找到报头和有效载荷之间的分隔符---如果字节流式的报文没读全就继续读string text_leninbuffer.substr(0,pos);//报头是有效载荷的长度int lenstoi(text_len);int totallentext_len.size()LINE_SEP_LEN*2len;//整个报文的长度if(inbuffer.size()totallen) {cout输入的消息不完整,请继续输入.continue...endl;continue;//报文没读完继续读}cout处理前的inbuffer: \ninbufferendl;*outinbuffer.substr(0,totallen);inbuffer.erase(0,totallen);cout处理后的inbuffer: \ninbufferendl;break;
}
else return false;
}return true;}介绍一下
在Request类内
外部传参两个操作数和一个运算符进来构造Request对象调用Serialize序列化函数构造字符串_xSEP_opSEP_y,其中SEP是空格, _x是左操作数, _op是运算符, _y是右操作数外部传参数字符串_xSEP_opSEP_y进来调用Deserialize反序列化通过该字符串构造内嵌参数 _x, _op, _y。注意的是运算符 _op在传入时会将符号转化成其对应的ASCII码。例如“会被转化成43”此时需要将字符串里的43取出来再转化成整形43方便后续转化回
在Response类内
外部传字符串_exitcodeSEP_result进来调用Deserialize反序列化函数构造内嵌参数 _exitcode和 _result。其中 exitcode是退出码当计算结果正确时退出码为NONE。计算时出现除零错误退出码为DIV_ZERO。计算时出现商零错误退出码为MOD_ZERO。传入参数时运算符传入错误退出码为OP_ERR _result是计算结果。调用Serialize序列化函数利用内嵌参数 _exitcode和 _result构造字符串 _exitcodeSEP_result enLength协议定制函数給传入的字符串加上报头传入字符串xSEPopSEPy加上报头后字符串为text_len\r\nxSEPopSEPy\r\n。其中xSEPopSEPy为有效载荷text_len为有效载荷的长度SEP是空格。 deLength协议定制函数。函数作用是給传入的字符串str去报头并把有效载荷通过ret传出去。传入的字符串是text_len\r\nxSEPopSEPy\r\n去掉报头后取出字符串 “xSEPopSEPy”。其中SEP是空格。 recvPackage函数是供服务器调用接收数据包的函数。服务器接收客户端发送来的报文若报文不符合定义的协议形式或者没读上来的报文不是完整的则阻塞式读取直到读上来完整的报文。然后通过输出型参数out将报文传出去。 calserver.cc #includecalserver.hpp
#includelog.hpp
#includeiostream
#includestdlib.h
#includememory
using namespace Server;
using namespace std;static void Usage(string proc)
{cout\nUsage:\n\tproc local_port\n\nendl;
}
//req是一个已经处理的完整的对象
bool cal(const Request req,Response rep)
//根据req填充rep
{
switch(req._op)
{case :rep._resultreq._xreq._y;break;case -:rep._resultreq._x-req._y;break; case *:rep._resultreq._x*req._y;break; case /:{if(req._y0) rep._exitcodeDIV_ZERO;elserep._resultreq._x/req._y;} break; case %:{if(req._y0) rep._exitcodeMOD_ZERO;elserep._resultreq._x%req._y;}break;default:rep._exitcodeOP_ERR;break;
}
return true;}int main(int argc,char* argv[])
{
if(argc!2)
{Usage(argv[0]);exit(USAGE_ERR);
}uint16_t portatoi(argv[1]);//将字符串转化为整数unique_ptrcalserver ts(new calserver(port));
ts-initserver();
ts-start(cal);return 0;
}cal是计算函数传入Request对象和Response对象。Request对象中参数有两个操作数 _ x、_ y 和一个运算符 _ op计算出结果放到Response对象的参数_result中退出码放到 _exitcode。 calserver.hpp #pragma once
#include iostream
#include string
#include cstring
#include cstdlib
#include unistd.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include sys/wait.h
#include signal.h
#include pthread.h
#includefunctional
#includelog.hpp
#includeprotocol.hpp
#define NUM 1024using namespace std;
static const int gbacklog 5;
namespace Server
{enum{USAGE_ERR1,SOCK_ERR,BIND_ERR,LISTEN_ERR};typedef functionbool(const Requestreq,Response res) func_t;void handlerentry(int sock,func_t func)
{string inbuffer;while(true){//1. 获取客户端发送来的数据报,确定数据报是带报头的数据报//text_len\r\nx op y\r\n string req_text,req_str;if(!recvPackage(sock,inbuffer,req_text)) return;cout带报头的请求数据报: req_textendl;//2.对数据报进行反序列化//text_len\r\nx op y\r\n - x op y---去掉报头取出里面的内容if(!deLength(req_text,req_str)) return;cout去掉报头的请求数据报:req_strendl;//走到这里再往下就卡主了只打印到上面那条日志后面都没打印到//3.获得一个结构化的请求对象Request req;if(!req.Deserialize(req_str)) return;//如果反序列化失败直接返回//4.对对象进行操作---进行服务器业务//4.1.获得一个结构化响应Response rep;func(req,rep);//5.对对象进行序列化//_exitcode _result -_exitcodeSEP_resultstring rep_str;rep.Serialize(rep_str);cout计算完成后的响应 rep_strendl;//6.給有效载荷加上报头//exitcode result - content_len\r\nexitcode result\r\nstring rep_textenLength(rep_str);cout加上报头的完整响应报文: rep_textendl;//7.把报文发送回給客户端send(sock,rep_text.c_str(),rep_text.size(),0);}}
typedef functionbool(const Requestreq,Response res) func_t;
class calserver
{public:
calserver(const uint16_t port):_port(port),_listensock(-1){}void initserver()
{
//1.创建套接字
_listensocksocket(AF_INET,SOCK_STREAM,0);
if(_listensock0)
{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_familyAF_INET;
local.sin_porthtons(_port);
local.sin_addr.s_addrINADDR_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 lensizeof(cli);bzero(cli,len);int sockaccept(_listensock,(struct sockaddr*)cli,len);if(sock0){logMessage(FATAL,accept client error);continue;}logMessage(NORMAL,accept client success);coutaccept sock: sockendl;//多进程版---//一个客户端占用一个文件描述符原因在于孙子进程执行IO任务需要占用独立的文件描述符,而文件描述符是继承父进程的而每次客户端进来都要占用新的文件描述符//因此若接收多个客户端不退出的话文件描述符会越来越少。pid_t idfork();//创建子进程if(id0)//子进程进入{close(_listensock);//子进程不需要用于监听因此关闭该文件描述符handlerentry(sock,fun);close(sock);exit(0);}//父进程close(sock);pid_t retwaitpid(id,nullptr,0);if(ret0){cout waitsuccess: ret endl;}}
}
~calserver(){}
private:
int _listensock;//用于监听服务器的sock文件描述符
uint16_t _port;//端口号
};}handlerentry函数是供服务器调用的接收发送函数。传入参数为用于通信的文件描述符sock调用的计算函数calcal在calserver.cc文件中)。服务器调用handlerentry函数接收到客户端发送来的报文形式形如text_len\r\nx op y\r\n调用deLength函数去报头转化后的字符串形如x op y调用Request对象的反序列函数Deserialize通过字符串x op y填充Request对象的 _ x、_ op、 _ y参数调用func即cal函数利用Request对象内的参数计算得出结果并构造Response对象调用Response对象的序列化函数将 参数_exitcode _result 转化为字符串 _exitcodeSEP_result;调用enLength函数加上报头转化后的字符串形如content_len\r\nexitcode result\r\n然后通过send函数将字符串发送給客户端。注意的是服务器是多进程版即能够与多个客户端进行并行通信。 calclient.cc #includeiostream
#includestring
#includememory
#includecalclient.hpp
using namespace std;
using namespace client;
static void Usage(string proc)
{cout\nUsage :\n\tproc serverip serverport\nendl;
}
int main(int argc, char* argv[])
{if(argc!3){Usage(argv[0]);exit(1);}string serveripargv[1];
uint16_t serverportatoi(argv[2]);unique_ptrcalclient tc(new calclient(serverip,serverport));tc-initclient();
tc-start();return 0;
}calclient.hpp #pragma once
#include iostream
#include string
#include cstring
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include unistd.h
#include ctype.h
#includeprotocol.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
_socksocket(AF_INET,SOCK_STREAM,0);
if(_sock0)
{cerrsocket create errorendl;exit(2);
}
//2.绑定 ip port不显示绑定OS自动绑定
}void start()
{
struct sockaddr_in ser;
bzero(ser,sizeof(ser));
socklen_t lensizeof(ser);
ser.sin_familyAF_INET;
ser.sin_porthtons(_port);
ser.sin_addr.s_addrinet_addr(_ip.c_str());
if(connect(_sock,(struct sockaddr *)ser,len)!0)
{cerrconnect errorendl;
}else
{string line;string inbuffer;while(true){coutmycal: ;//输入xopygetline(cin,line);Request reqParseLine(line);//用xopy取出x op y构造Request对象string context;req.Serialize(context);//序列化用x op y构造字符串xSEPopSEPystring send_strenLength(context);//定制协议---x op y-text_len\r\nx op y\r\n---給内容加上报头coutcalclient send str: send_strendl;send(_sock,send_str.c_str(),send_str.size(),0);//客户端把报文发送給服务器string package;if(!recvPackage(_sock,inbuffer,package)) continue;//服务器处理完数据客户端接收服务器发送来的报文// content_len\r\nexitcode result\r\nstring reser_len;if(!deLength(package,reser_len)) continue;//去报头// content_len\r\nexitcode result\r\n - exitcode resultResponse rep;rep.Deserialize(reser_len);//反序列化//_exitcodeSEP_result-_exitcode _resultcout_exitcode: rep._exitcodeendl;cout_result: rep._resultendl;}
}
}~calclient()
{if(_sock0) close(_sock);
}Request ParseLine(const string line)
{//xopy-取出来到x op y 上
int i0;
int status0;
int numline.size();
string left,right;
char op;while(inum)
{
switch(status)
{case 0:{if(!isdigit(line[i])){opline[i];//取出运算符**status1;}elseleft.push_back(line[i]);//取出左操作数}break;case 1:i;status2;break;case 2:right.push_back(line[i]);break;
}
}
coutleft: stoi(left) op: op right: stoi(right)endl;
return Request(stoi(left),stoi(right),op);//返回Request对象}private:
int _sock;
uint16_t _port;
string _ip;};
}
ParseLine函数接收形如11的字符串解析字符串后构造Request对象客户端调用start函数接收命令行发送来的形如11的字符串然后调用ParseLine函数构造Request对象。调用Request对象的序列化函数Serialize构造字符串xSEPopSEPy。调用协议定制函数enLength給字符串加上报头转化后的字符串为text_len\r\nx op y\r\n调用send函数将字符串发送給服务器服务器计算完成后将结果发送回来调用recvPackage函数接收服务器发送回来的字符串字符串形如 “content_len”\r\nexitcode result\r\n;调用deLength函数去报头转化后的字符串形如exitcode result调用Response对象的反序列化函数Deserialize通过字符串获取参数_exitcode _result并进行打印。 makefile .PHONY:all
all:calclient calservercalclient:calclient.ccg -o $ $^ -stdc11calserver:calserver.ccg -o $ $^ -stdc11 .PHONY:clean
clean:rm -rf calserver calclient利用Json进行序列化和反序列化
JSONJavaScript Object Notation是一种轻量级的数据交换格式它以易于阅读和写作的文本形式表示结构化数据。JSON由两种结构构成键值对键值对集合和值的有序列表。在这里以键值对的方式使用。
json库的安装
输入以下指令安装
sudo yum install -y jsoncpp-devel安装完后可通过ls查询 makefile ccg
LD-DMYPRO
.PHONY:all
all:calclient calservercalclient:calclient.cc$(cc) -o $ $^ -stdc11 -ljsoncpp #${LD}calserver:calserver.cc$(cc) -o $ $^ -stdc11 -ljsoncpp #${LD}.PHONY:clean
clean:rm -rf calserver calclient如果不使用Json序列化就注释掉第二行LD-DMYPRO protocol.hpp #pragma once
#includeiostream
#includestring
#include sys/types.h
#include sys/socket.h
#include cstring
#include jsoncpp/json/json.h
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 {NONE0,DIV_ZERO,MOD_ZERO,OP_ERR
};
//x op y-text_len\r\nx op y\r\n---給内容加上报头
std::string enLength(const std::string text)//协议定制
{std::string send_strto_string(text.size());send_strLINE_SEP;send_strtext;send_strLINE_SEP;return send_str;
}
//text_len\r\nx op y\r\n - x op y---去掉报头取出里面的内容
bool deLength(const std::string str,string* ret)//协议定制
{auto itstr.find(LINE_SEP);//找到报头if(itstd::string::npos) return false;//如果没找到则直接返回int lenstoi(str.substr(0,it));//取出字符串的长度*retstr.substr(itLINE_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;*outto_string(_x);*outSEP;*outto_string(_op);*outSEP;*outto_string(_y);
#else
Json::Value root;//json的对象是键值对[key,value]
root[first]_x;//int类型被设置进json的键值对时自动转换为string类型
root[second]_y;
root[oper]_op;Json::FastWriter writer;*outwriter.write(root);//调用接口序列化返回值为字符串#endifreturn true;
}bool Deserialize( const string origin)//反序列化将传过来的字符串拆出来传参給_x _op _y
{//_xSEP_opSEP_y- _x,_op,_y#ifdef MYPROauto leftitorigin.find(SEP);coutDeserialize找到了leftSEP: leftitendl;auto rightitorigin.rfind(SEP);coutDeserialize找到了rightSEP: rightitendl;if(leftitstring::npos|| rightitstring::npos) return false;if(leftitrightit) return false;int opsizerightit-leftit-1;coutopsize: opsizeendl;
//1 43 1--leftit1,rightit4,opsizerightit-leftit-14-1-12;
//1 3 1--leftit1,right3,opsizerightit-leftit-13-1-11// if(rightit-(leftitSEP_LEN)!1) return false;if(rightit-(leftitSEP_LEN)!opsize) return false;//号ASCII码是43从char转int被解析成43即stringlen为两位,这里的运算rightit-(leftitSEP_LEN)!1就出问题
//4-(11)2;3-(11)1std::string origin_xorigin.substr(0,leftit);std::string origin_yorigin.substr(rightitSEP_LEN);if(origin_x.empty()) return false;if(origin_y.empty()) return false;coutorigin_x: origin_x origin_y: origin_yendl;_xstoi(origin_x);int opfstoi(origin.substr(leftit,rightit));_opopf;coutopf: opf_op: _opendl;_ystoi(origin_y);
#else
Json::Value root;
Json::Reader reader;
reader.parse(origin,root);//反序列化将字符串中的协议字符串填进对象对应的元素中_xroot[first].asInt();
_yroot[second].asInt();
_oproot[oper].asInt();
#endifreturn 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;
*outto_string(_exitcode);
*outSEP;
*outto_string(_result);
#else
Json::Value root;
root[exitcode]_exitcode;
root[result]_result;Json::FastWriter writer;
*out writer.write(root);
#endifreturn true;
}bool Deserialize(const string in)//反序列化
{//_exitcodeSEP_result-_exitcode _result#ifdef MYPRO
auto posin.find(SEP);
if(posstring::npos) return false;string excstrin.substr(0,pos);
string resstrin.substr(posSEP_LEN);
if(excstr.empty()||resstr.empty()) return false;_exitcodestoi(excstr);
_resultstoi(resstr);#else
Json::Value root;
Json::Reader reader;
reader.parse(in,root);
_exitcoderoot[exitcode].asInt();
_resultroot[result].asInt();
#endif
return true;}public:
int _exitcode;//退出码
int _result;//结果
};//text_len\r\nx op y\r\n
bool recvPackage(int sock,string inbuffer,string*out)
{
char buffer[1024];while(true)
{
ssize_t srecv(sock,buffer,sizeof(buffer)-1,0);
if(s0)
{buffer[s]0;inbufferbuffer;auto posinbuffer.find(LINE_SEP);if(posstring::npos)continue;//没找到报头和有效载荷之间的分隔符---如果字节流式的报文没读全就继续读string text_leninbuffer.substr(0,pos);//报头是有效载荷的长度int lenstoi(text_len);int totallentext_len.size()LINE_SEP_LEN*2len;//整个报文的长度if(inbuffer.size()totallen) {cout输入的消息不完整,请继续输入.continue...endl;continue;//报文没读完继续读}cout处理前的inbuffer: \ninbufferendl;*outinbuffer.substr(0,totallen);inbuffer.erase(0,totallen);cout处理后的inbuffer: \ninbufferendl;break;
}
else return false;
}return true;}Json序列化在Request类和Response类中使用。
条件编译
#ifdef指令说明如果预处理已经定义了后面的标识符DEBUG即DEBUG为真则执行 #ifdef 与 #else 之间的所有所有代码不执行#else之后的代码。若DEBUFG为未定义即DEBUG为假则执行#else与#endif之间的代码。#endif 用于结束该条件编译指令。#ifdef和#endif搭配使用。
格式
#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;*outwriter.write(root);//调用接口序列化返回值为字符串创建一个Json的Value对象键为first对应的值为_x,将操作数和运算符设置进Value对象里然后通过Json的FastWriter对象调用write进行序列化。
Request类内的Json反序列化片段-Deserialize
Json::Value root;
Json::Reader reader;
reader.parse(origin,root);//反序列化将字符串中的协议字符串填进对象对应的元素中_xroot[first].asInt();
_yroot[second].asInt();
_oproot[oper].asInt();创建一个Json的Value对象然后再创建一个Json的Reader对象调用Reader对象的parse把携带协议的字符串填进Value对象对应的元素里。然后再通过键值对的方式把元素取出。