wordpress友情链接主题,公司seo是什么级别,目录更新 wordpress,要给公司做一个网站怎么做的吗目录
再谈 协议
结构化数据的传输
序列化和反序列化
网络版计算器
封装套接字操作
服务端代码
服务进程执行例程
启动网络版服务端
协议定制
客户端代码
代码测试
使用JSON进行序列化与反序列化 我们程序员写的一个个解决我们实际问题#xff0c;满…目录
再谈 协议
结构化数据的传输
序列化和反序列化
网络版计算器
封装套接字操作
服务端代码
服务进程执行例程
启动网络版服务端
协议定制
客户端代码
代码测试
使用JSON进行序列化与反序列化 我们程序员写的一个个解决我们实际问题满足我们日常需求的网络程序都是在应用层。
再谈 协议
我们之前讲过协议是一种 约定。网络协议是通信计算机双方必须共同遵从的一组约定只有通信计算机双方都遵守相同的协议计算机之间才能互相通信交流。
结构化数据的传输
socket api的接口在读写数据时都是按 字符串 的方式来发送接收的。
如果需要传输的数据是一个字符串那么直接将这一个字符串发送到网络当中此时对端也能从网络当中获取到这个字符串。但如果需要传输的是一些结构化的数据此时就不能将这些数据一个个发送到网络当中。
那么如果我们要传输一些结构化的数据 怎么办呢?
例如我们需要实现一个服务器版的加法器。我们需要客户端把要计算的两个加数发过去然后由服务器进行计算最后再把结果返回给客户端。那么客户端每次给服务端发送的请求数据当中就需要包括左操作数、右操作数以及对应需要进行的操作此时客户端要发送的就不是一个简单的字符串而是一组结构化的数据。
当客户端选择将结构化的数据逐一发送到网络中服务端接收数据的过程也会相应地碎片化。每次从网络中接收到一部分数据服务端都需要对这些离散的信息进行整理尝试将它们重新组合成原始的结构化数据。这个过程既复杂又容易出错因为数据可能在传输过程中出现丢失或顺序混乱的情况。
因此为了简化数据传输和处理的流程客户端通常会采取“打包”策略。打包意味着将多个相关的数据元素组合成一个整体然后再进行传输。这样服务端每次从网络中接收到的都是一个完整的数据包其中包含了所有必要的信息。
客户端常见的“打包”方式主要有两种
约定方案一:
客户端发送一个形如11的字符串;这个字符串中有两个操作数, 都是整形;两个数字之间会有一个字符是运算符, 运算符只能是 ;数字和运算符之间没有空格;
客户端能够将结构化的数据编排成一个字符串格式并通过网络将其发送出去。当服务端从网络接收到这个字符串时它会采用与客户端相同的解析方法从而从这个字符串中提取出原始的结构化数据。这样的通信方式确保了数据的完整性和准确性在客户端和服务端之间的传输。
约定方案二:
定义结构体来表示我们需要交互的信息;发送数据时将这个结构体按照一个规则转换成字符串接收到数据的时候再按照相同的规则把字符串转化回结构体;这个过程叫做 序列化 和 “反序列化”
客户端可以设计一个特定的结构体将需要交互的信息定义到这个结构体当中。在发送数据前客户端会利用序列化技术将这个数据结构转换成一种统一的、可传输的字符串或字节流格式。当服务端接收到这些数据后它会利用反序列化技术将这个字符串或字节流还原成原始的数据结构。通过这种方式服务端可以轻松地提取出客户端发送的信息并进行相应的处理。这种序列化和反序列化的过程确保了数据在不同系统间的兼容性和可交换性。
序列化和反序列化
序列化是将对象的状态信息转换为可以存储或传输的形式字节序列的过程。反序列化是把字节序列恢复为对象的过程。
OSI七层模型中的表示层的主要任务是将设备内部特有的数据格式即应用层上的数据格式转换为符合网络传输标准的格式。这种网络标准数据格式通常是通过序列化过程得到的使得数据能够以一致和可理解的方式在网络中进行传输。
网络版计算器
封装套接字操作
由于服务端和客户端都需要创建套接字以及使用套接字完成一些固定的操作因此我们实现一个简单的TCP套接字socket类的实现它封装了套接字的基本操作包括创建、绑定、监听、接受连接和连接。这样服务端和客户端都可以直接调用这些函数。封装套接字操作可以使服务端和客户端代码更整洁、可重用并减少重复代码。
Socket.hpp
#pragma once#include iostream
#include string
#include unistd.h
#include cstring
#include sys/types.h
#include sys/stat.h
#include sys/socket.h
#include arpa/inet.h
#include netinet/in.h
#include Log.hppenum{SocketErr 2,BindErr,ListenErr,AcceptErr
};#define backlog 10class Sock
{
public:Sock(){}~Sock(){}
public:void Socket(){sockfd_ socket(AF_INET,SOCK_STREAM,0);if(sockfd_ 0){lg(Fatal,socker error, %s:%d,strerror(errno),errno);exit(SocketErr);} }void Bind(uint16_t port){struct sockaddr_in local;memset(local,0,sizeof(local));local.sin_family AF_INET;local.sin_port htons(port);local.sin_addr.s_addr INADDR_ANY;if(bind(sockfd_,(struct sockaddr *)local,sizeof(local)) 0){lg(Fatal,bind error, %s: %d, strerror(errno), errno);exit(BindErr);}}void Listen(){if(listen(sockfd_ , backlog) 0){lg(Fatal,listen error, %s: %d, strerror(errno), errno);exit(ListenErr);}}int Accept(std::string* clientip, std::uint16_t* clientport){struct sockaddr_in peer;socklen_t len sizeof(peer);int newfd accept(sockfd_,(struct sockaddr*)peer,len);if(newfd 0){lg(Warning,accept error, %s: %d, strerror(errno), errno);exit(AcceptErr);}char ipstr[64];inet_ntop(AF_INET,(peer.sin_addr.s_addr),ipstr,sizeof(ipstr));*clientip ipstr;*clientport ntohs(peer.sin_port);return newfd;}bool Connect(const std::string ip, const uint16_t port){struct sockaddr_in peer;memset(peer,0,sizeof(peer));peer.sin_family AF_INET;peer.sin_port htons(port);inet_pton(AF_INET,ip.c_str(),(peer.sin_addr.s_addr));int n connect(sockfd_,(struct sockaddr*)peer,sizeof(peer));if(n -1){std::cerr connect to ip : port error std::endl;return false;}return true;}void Close(){close(sockfd_);}int FD(){return sockfd_;}private:int sockfd_;
};
服务端代码
首先我们需要初始化服务器这包括三个关键步骤
使用socket函数来创建一个新的套接字。接着通过bind函数我们将这个套接字绑定到一个特定的端口号上这样客户端就可以通过这个端口与服务器建立连接。然后通过调用listen函数我们将套接字设置为监听状态等待客户端的连接请求。
服务器初始化完成后就可以启动它了。启动后服务器的主要任务是不断地调用accept函数从监听套接字中接收新的连接请求。每当成功接受到一个新连接时服务器会创建一个新的进程。这个新进程将负责为该客户端提供计算服务确保每个客户端都能得到及时且独立的响应。
TcpServer.hpp
#pragma once#include iostream
#include signal.h
#include unistd.h
#include functional
#include Socket.hpp//这允许我们为 TCP 服务器提供一个自定义的回调函数该函数处理从客户端接收到的数据。
using func_t std::functionstd::string(std::string package); //std::function对象该对象接受一个 std::string 引用作为参数并返回一个 std::stringclass TcpServer
{
public:TcpServer(uint16_t port,func_t callback):port_(port),callback_(callback){}//初始化tcp服务器bool InitServer(){listensock_.Socket();listensock_.Bind(port_);listensock_.Listen();lg(Info,init server .... done);return true;}//启动服务器void Start(){ signal(SIGCHLD, SIG_IGN);//忽略了 SIGCHLD 和 SIGPIPE 信号当子进程终止或管道写入失败时服务器不会接收到这些信号。signal(SIGPIPE, SIG_IGN);//无限循环在该循环中它尝试接受新的客户端连接。对于每个新的连接它创建一个子进程来处理该连接。//并使用之前提供的回调函数来处理这些数据。如果回调函数返回一个非空字符串那么该字符串将被发送回客户端。while (true){std::string clientip;uint16_t clientport;int sockfd listensock_.Accept(clientip,clientport);if(sockfd 0)continue;// 返回继续监听 lg(Info,accept a new link, sockfd: %d, clientip: %s, clientport: %d,sockfd, clientip, clientport);// 提供服务if(fork() 0)//{listensock_.Close();//在子进程中服务器不再需要监听套接字调用 listensock_.Close(); 关闭监听套接字释放相关的系统资源。 std::string inbuffer_stream;//数据计算while (true){char buffer[1280];ssize_t n read(sockfd,buffer,sizeof(buffer));if(n 0) {buffer[n] 0;inbuffer_stream buffer;while(true){std::string info callback_(inbuffer_stream);if(info.empty())break;write(sockfd, info.c_str(), info.size());}}else if(n 0)break;elsebreak;} exit(0);//}close(sockfd);} }~TcpServer(){}
private:uint16_t port_;Sock listensock_;func_t callback_;
};
说明一下
当前服务器采用的是多进程的方案对于每个新的连接创建一个子进程来处理该连接。 提供的回调函数来处理客户端发送过来的数据。如果回调函数返回一个非空字符串那么该字符串将被发送回客户端。
服务进程执行例程
当服务端调用accept函数获取到新连接并创建新进程后该线程就需要为该客户端提供计算服务此时该进程需要先读取客户端发来的计算请求然后进行对应的计算操作如果客户端发来的计算请求存在除0、模0、非法运算等问题就将响应结构体当中的状态字段对应设置为1、2、3即可。
ServerCal.hpp
#pragma once
#include iostream
#include Protocol.hppenum
{DivZero 1,ModZero,Other_Oper
};class ServerCal
{
public:ServerCal(){}Response CalculatorHelper(const Request req){Response resp(0, 0);switch (req.op){case :resp.result req.x req.y;break;case -:resp.result req.x - req.y;break;case *:resp.result req.x * req.y;break;case /:{if (req.y ! 0)resp.result req.x / req.y;elseresp.code DivZero;}break;case %:{if (req.y ! 0)resp.result req.x % req.y;elseresp.code ModZero;}break;default:resp.code Other_Oper;break;}return resp;}// len\n10 20\nstd::string Calculator(std::string package){std::string content;bool r Decode(package, content); // len\n10 20\nif (!r)return ;// 10 20Request req;r req.Deserialize(content); // 反序列化完req中的变量就拿到值了if (!r)return ;content ; //清空Response resp CalculatorHelper(req); // result30 code0;// 计算完进行序列化resp.Serialize(content);content Encode(content);return content;}~ServerCal(){}
};
启动网络版服务端
ServerCal.cpp
前面我们在TcpServer.hpp封装了服务器初始化和启动服务器函数的类以及ServerCal类实现网络版计算器的类执行例程。下面我们实现一个ServerCal.cpp来启动网络版服务器只有要调用前面两个类实现的接口即可。
从命令行参数获取端口号创建ServerCal实例绑定ServerCal的Calculator方法创建TcpServer实例并将绑定的Calculator方法和端口号作为参数传递给它。调用InitServer方法初始化服务器最后调用Start方法启动服务器。
#include TcpServer.hpp
#include ServerCal.hppvoid Usage(const std::string proc)
{std::cout \nUsage: proc port\n std::endl;
}int main(int argc, char *argv[])
{if(argc ! 2){Usage(argv[0]);exit(0);}uint16_t port std::stoi(argv[1]);ServerCal cal;//std::bind是C标准库中的一个函数模板它可以将一个可调用对象如函数、lambda函数或成员函数指针与其参数绑定生成一个新的可调用对象。//ServerCal::Calculator是ServerCal类中的Calculator成员函数的指针。//cal是ServerCal类的一个实例的地址该实例用于调用Calculator成员函数。//std::placeholders::_1是一个占位符它表示bind生成的新可调用对象接受的第一个参数将传递给Calculator成员函数作为它的第一个参数。TcpServer *tsvp new TcpServer(port,std::bind(ServerCal::Calculator,cal,std::placeholders::_1));tsvp-InitServer();tsvp-Start();return 0;
}
协议定制 为了实现一个网络版的计算器确保通信双方遵循共同的规则和约定是至关重要的。这就需要我们制定一套简明的协议。数据的交互通常涉及请求数据和响应数据因此需要分别定义两者的结构。在实现层面C允许通过类来组织代码和数据但同样也可以使用更简单的结构体来定义数据结构。考虑到简洁性和直接性这里我们选择使用结构体来定义请求和响应的数据格式。 因此我们需要设计一个请求结构体用于封装从客户端发送到服务器的计算请求信息以及一个响应结构体用于封装服务器处理完请求后返回给客户端的结果。通过这种方式我们可以确保通信双方按照预定的格式发送和接收数据从而实现网络计算器的功能。
请求结构体中需要包括两个操作数以及对应需要进行的操作。响应结构体中需要包括一个计算结果除此之外响应结构体中还需要包括一个状态字段表示本次计算的状态因为客户端发来的计算请求可能是无意义的。请求结构体和响应结构体当中都封装了序列化函数和反序列化函数。我们在类外设置了编码函数和解码函数
#pragma once#include iostream
#include stringconst std::string blank_space_sep ;
const std::string protocol_sep \n;std::string Encode(std::string content)
{std::string package std::to_string(content.size());package protocol_sep;package content;package protocol_sep;return package;
}// len\nx op y\nXXXXXX
// protocolnumber\nlen\nx op y\nXXXXXX
bool Decode(std::string package, std::string *content)
{size_t pos package.find(protocol_sep);if(pos std::string::npos) return false;std::string len_str package.substr(0,pos);std::size_t len std::stoi(len_str);std::size_t total_len len_str.size() len 2;if(package.size() total_len) return false;//传入的序列化字符串没有达到报头提供的字符串长度*content package.substr(pos1,len);package.erase(0,total_len);//return true;
}class Request
{
public:Request(int data1,int data2,char oper):x(data1),y(data2),op(oper){}Request()//{}
public:bool Serialize(std::string *out){// 构建报文的有效载荷// struct string, x op ystd::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;}bool Deserialize(const std::string in)// x op y{std::size_t left in.find(blank_space_sep);if(left std::string::npos)return false;std::string part_x in.substr(0,left);std::size_t right in.rfind(blank_space_sep);if(right std::string::npos)return false;std::string part_y in.substr(right);if(left2 ! right)return false;op in[left 1];x std::stoi(part_x);x std::stoi(part_y);return true;}void DebugPrint(){std::cout 新请求构建完成: x op y ? std::endl;}public://x op yint x;int y;char op;// - * / %
};class Response
{
public:Response(int res,int c):result(res),code(c){}Response(){}
public:bool Serialize(std::string *out){// result code// 构建报文的有效载荷std::string s std::to_string(result);s blank_space_sep;s std::to_string(code);*out s;return true;}bool Deserialize(const std::string in){std::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(pos1);result std::stoi(part_left);code std::stoi(part_right);return true;}void DebugPrint(){std::cout 结果响应完成, result: result , code: code std::endl;}public:int result;int code;//0表示结果是可信的否则!0具体是几表明对应的错误原因
};请求结构体 序列化函数用于构建报文的有效载荷将 Request 对象转换为一个字符串。它首先将 x 和 y 转换为字符串并使用空格和操作符 op 将它们连接在一起。例如如果 x 是 5y 是 3并且 op 是 则生成的字符串将是 5 3反序列化函数尝试从一个字符串中恢复一个 Request 对象。它首先查找空格和操作符来分隔 x、op 和 y。然后它将这些部分转换回它们的原始类型并检查字符串的格式是否正确。如果一切正常它将更新 Request 对象的 x、y 和 op。 响应结构体 Serialize 函数接受一个指向 std::string 的指针 out并将 result 和 code 成员变量的值转换为字符串然后用空格blank_space_sep分隔它们并将结果字符串存储在 out 所指向的位置。函数总是返回 true表示序列化操作总是成功的。Deserialize 函数接受一个常量字符串引用 in并尝试从中解析出 result 和 code 的值。它首先查找空格的位置然后提取空格前后的两个子字符串并将它们分别转换为整数来更新 result 和 code 的值。如果字符串中没有找到空格函数返回 false否则返回 true。 编码函数 Encode: 函数接受一个字符串 content并返回一个编码后的字符串 package。首先将 content 的大小长度转换为字符串并添加到 package。 然后添加一个换行符。 接着添加原始的 content。 最后再添加一个换行符。这样编码后的字符串格式是length\ncontent\n。 解码函数 Decode: 这个函数尝试从给定的 package 字符串中解码出 content。它首先查找换行符来确定 content 的长度并检查 package 是否包含足够的数据。如果成功它会提取 content 并从 package 中删除已解码的部分。 注意 编码函数和解码函数是多个结构体或类都可能需要的共同操作因此将它们放在类外作为独立的函数。这种做法不仅增强了代码的可重用性还方便了协议的编码和解码逻辑的更换。通过将编码和解码逻辑与具体的数据结构分离我们可以在不修改数据结构定义的情况下更换编码和解码的实现从而实现了更好的模块化和可扩展性。 规定状态字段对应的含义 状态字段为0表示计算成功。状态字段为1表示出现除0错误。状态字段为2表示出现模0错误。状态字段为3表示非法计算。 此时我们就完成了协议的设计但需要注意只有当响应结构体当中的状态字段为0时计算结果才是有意义的否则计算结果无意义。
客户端代码
客户端首先也需要进行初始化
调用socket函数创建套接字。
客户端初始化完毕后需要调用connect函数连接服务端当连接服务端成功后客户端就可以向服务端发起计算请求了。这里可以让用户输入两个操作数和一个操作符构建一个计算请求然后将该请求发送给服务端。而当服务端处理完该计算请求后会对客户端进行响应因此客户端发送完请求后还需要读取服务端发来的响应数据。
客户端在向服务端发送或接收数据时可以使用write或read函数进行发送或接收也可以使用send或recv函数对应进行发送或接收。
连接服务器
由于我们前面封装了TCP套接字socket类的实现这里我们之间调用我们封装的接口即可下面是客户端代码
客户端代码
#include iostream
#include string
#include time.h
#include assert.h
#include Protocol.hpp
#includeSocket.hppvoid Usage(const std::string proc)
{std::cout \nUsage: proc serverip serverport\n std::endl;
}// ./clientcal ip port
int main(int argc, char *argv[])
{if (argc ! 3){Usage(argv[0]);exit(0);}std::string serverip argv[1];uint16_t serverport std::stoi(argv[2]);//创建套接字Sock sockfd;sockfd.Socket();//链接服务器bool r sockfd.Connect(serverip,serverport);if(!r) return 1;srand(time(nullptr)^getpid());int cnt 1;const std::string opers -*/%-^;std::string inbuffer_stream;while(cnt 10){//准备数据std::cout 第 cnt 次测试....., std::endl;int x rand() % 100 1;usleep(1000);int y rand() % 100 1;usleep(1000);char op opers[rand() % opers.size()];Request req(x,y,op);req.DebugPrint();//客户端发送请求std::string packge;req.Serialize(packge);packge Encode(packge);write(sockfd.FD(),packge.c_str(),packge.size());//接受请求响应char buffer[128];ssize_t n read(sockfd.FD(),buffer,sizeof(buffer));// 我们也无法保证我们能读到一个完整的报文if(n 0){buffer[n] 0;inbuffer_stream buffer;// len\nresult code\nstd::cout inbuffer_stream std::endl;std::string content;bool r Decode(inbuffer_stream,content);// result codeassert(r);Response resp;r resp.Deserialize(content);assert(r);resp.DebugPrint();}std::cout std::endl;sleep(1);cnt;}sockfd.Close();return 0;
}
代码测试
运行服务端后再让客户端连接服务端此时服务端就会对客户端发来的计算请求进行处理并会将计算后的结果响应给客户端。
我们看到如果客户端要进行除0、模0、非法运算在服务端识别后就会按照约定对应将响应数据的状态码设置为1、2、3此时响应状态码为非零因此在客户端打印出来的计算结果就是没有意义的。 此时我们就以这样一种方式约定出了一套应用层的简单的网络计算器这就叫做协议。
使用JSON进行序列化与反序列化
上面我们进行序列化和反序列化是自己进行协议定制其实我们也可以用JSON或者Protobuf进行数据的序列化和反序列化操作。
JSON (JavaScript Object Notation) 和 Protobuf (Protocol Buffers) 都是数据序列化格式但它们在设计目标、性能、使用场景等方面有所不同。
下面我们主要来介绍一下使用JSON进行序列化和反序列化操作
JSON
设计目标JSON 主要用于人类可读性和易于编写。它是基于 JavaScript 的子集但不仅限于 JavaScript 使用。性能JSON 的解析和序列化速度相对较慢尤其是对于大型数据结构。使用场景JSON 广泛用于 API 通信、配置文件、Web 存储等场景因为它易于阅读和编写并且跨语言、跨平台。 在使用 JsonCpp 之前你需要确保已经安装了这个库。 sudo yum install -y jsoncpp-devel 安装完成后项目中加入头文件#include jsoncpp/json/json.h 编译命令后面加上-ljsoncpp 下面是一个简单的示例展示了如何使用 JsonCpp 来解析和生成 JSON 数据
#include iostream
#include jsoncpp/json/json.h
#include unistd.hint main() { // 创建一个 JSON 对象 Json::Value root; // 将用于存储 JSON 数据的根对象 root[x] 40; root[y] 30; root[op] ; root[desc] this is a oper; // 序列化将 JSON 对象转换为字符串 Json::FastWriter writer; //Json::StyledWriter writer; //StyledWriter比Fastwriter多加了\n可读性比较好std::string jsonString writer.write(root); // 输出 JSON 字符串 std::cout JSON string: jsonString std::endl; sleep(3);// 反序列化从字符串解析 JSON Json::Value v; Json::Reader Reader; Reader.parse(jsonString,v);// 访问 JSON 对象中的值 int x v[x].asInt(); int y v[y].asInt(); char op v[op].asInt(); std::string desc v[desc].asString(); // 输出解析后的值 std::cout x std::endl; std::cout y std::endl; std::cout op std::endl; std::cout desc std::endl; return 0;
}运行结果 有了上面对JSON基本使用的理解后下面我们在网络版计算器的协议定制的代码中增加JSON方式的序列化与反序列化
我们根据是否定义了MySelf宏来选择使用两种序列化方式
#pragma once#include iostream
#include string
#include jsoncpp/json/json.h// #define MySelf 1const std::string blank_space_sep ;
const std::string protocol_sep \n;std::string Encode(std::string content)
{std::string package std::to_string(content.size());package protocol_sep;package content;package protocol_sep;return package;
}// len\nx op y\nXXXXXX
// protocolnumber\nlen\nx op y\nXXXXXX
bool Decode(std::string package, std::string *content)
{size_t pos package.find(protocol_sep);if(pos std::string::npos) return false;std::string len_str package.substr(0,pos);std::size_t len std::stoi(len_str);std::size_t total_len len_str.size() len 2;if(package.size() total_len) return false;//传入的序列化字符串没有达到报头提供的字符串长度*content package.substr(pos1,len);package.erase(0,total_len);//return true;
}// json, protobuf
class Request
{
public:Request(int data1,int data2,char oper):x(data1),y(data2),op(oper){}Request()//{}
public:bool Serialize(std::string *out){
#ifdef MySelf// 构建报文的有效载荷// struct string, x op ystd::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;
#elseJson::Value root;root[x] x;root[y] y;root[op] op;// Json::FastWriter w;Json::StyledWriter w;*out w.write(root);return true;
#endif}bool Deserialize(const std::string in)// x op y{
#ifdef MySelfstd::size_t left in.find(blank_space_sep);if(left std::string::npos)return false;std::string part_x in.substr(0,left);std::size_t right in.rfind(blank_space_sep);if(right std::string::npos)return false;std::string part_y in.substr(right);if(left2 ! right)return false;op in[left 1];x std::stoi(part_x);y std::stoi(part_y);return true;
#elseJson::Value root;Json::Reader r;r.parse(in,root);x root[x].asInt();y root[y].asInt();char op root[op].asInt();return true;
#endif}void DebugPrint(){std::cout 新请求构建完成: x op y ? std::endl;}public://x op yint x;int y;char op;// - * / %
};class Response
{
public:Response(int res,int c):result(res),code(c){}Response(){}
public:bool Serialize(std::string *out){
#ifdef MySelf// result code// 构建报文的有效载荷std::string s std::to_string(result);s blank_space_sep;s std::to_string(code);*out s;return true;
#elseJson::Value root;root[result] result;root[code] code;// Json::FastWriter w;Json::StyledWriter w;*out w.write(root);return true;
#endif}bool Deserialize(const std::string in){
#ifdef MySelfstd::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(pos1);result std::stoi(part_left);code std::stoi(part_right);return true;
#elseJson::Value root;Json::Reader r;r.parse(in,root);int result root[result].asInt();int code root[code].asInt();
#endif}void DebugPrint(){std::cout 结果响应完成, result: result , code: code std::endl;}public:int result;int code;//0表示结果是可信的否则!0具体是几表明对应的错误原因
};makefile文件
编译时要加上-ljsoncpp选项我们也可以在makefile文件中进行宏定义Myself
.PHONY:all
all:servercal clientcal# Flag -DMySelf1
Flag #-DMySelf1
Lib-ljsoncppservercal:ServerCal.cppg -o $ $^ -stdc11 $(Lib) $(Flag)
clientcal:ClientCal.cppg -o $ $^ -stdc11 $(Lib) $(Flag).PHONY:clean
clean:rm -f servercal clientcal
代码测试 以上我们就成功用Json实现了数据序列化和反序列化。