- 序列化和反序列化
- 网络计算器
- 没有使用组件
- jsoncpp组件的使用
- jsontest
- 网络在线计算器
自顶向下去写,我们现在每次写的协议都是基于上一层的协议 序列化和反序列化
我们程序员写的一个一个程序,都是在应用层
我们发送的数据都是结构化的数据,这种结构话的数据就很好看也很好使用
struct message
{
我的昵称:会跳的鹅
我的头像:唐老鸭.png
我的消息:在吗?
消息时间:2022-07-10 11:31:59
}
这就是一个结构化的消息,我们要把这个结构化的数据转化成一个从“字符串”,我们要把这个结构化的数据
“{昵称:xxx,头像:xxx,消息:xxx}”,把它传送到网络里面,到对端之后,再把它解析出来,读取到对应的结构体里面
序列化:把结构化的数据转化为字符串的过程就是序列化的过程,
反序列化:把字符串转化为结构体信息就是反序列化的过程,因为结构化的数据,再网络里面不方便传输,字符串便于网络传输
为什么要进行序列化和反序列化
- 为了应用层网络通信的方便
- 为了方便上层进行使用内部成员,将应用和网络进行解耦,我们只关心使用,不关心发送的过程
而我们之前使用的TCP和UDP是没有序列化的过程,我们必须要有结构化的数据
而这些序列化和反序列化的数据,它实际上就是协议的表现
我们自己写一个实现序列化和反序列化
也可以直接使用别人写好的组件,java里面(json,xml,protobuff)
我们这里就使用jsoncpp
jsoncpp的安装
sudo yum install -y jsoncpp-devel网络计算器
我们这里自己定制协议
没有使用组件发送端口
伪代码
string x="123"; string opt="+"; string y="321"; string data=x+opt+y;// send(data);//这个就是序列化的过程,转化成字符串再发送过去
接收端
recv(data);
recv="123+321";
int opt==recv.find("+");
string x=recv.substr(0,opt);//
string y=recv.substr(opt);
int _x=to_int(x);
int _y=to_int(y);
int z=x+y;
string _z=to_string(z)
send(z)
//这就是一个反序列化的过程,接收到了一个字符串,再把它弄成结构体
//最后还需要再序列化
这个方案,我们是把数据分开了
我们发现这个过程实际上是非常麻烦的 ,都由我们自己做就很麻烦
约定方案2
- 定义结构体标识我们需要交互的信息
- 发送数据时将这个结构i体按照一个规则转化成字符串,接收数据的时候再按照相同的规则把字符串转化为结构体
- 这个过程就叫做“序列化”和“反序列化的过程”
struct request
{
int x;
int y;
char op;
}
struct request req={10,20,"+"};
write(sock,&req,sizeof(req));//这样子发送的时候,实际上就是直接把序列化的数据发送过去了
//接收
struct request req;
read(sock,&req,sizeof(req));//这样的话就缺少了序列化的过程,不太推荐
jsontest
测试json
t e s t . c p p test.cpp test.cpp
#include#include #include using namespace std; //仅仅是了解一下序列化和反序列化的过程 struct request_t { int x; // 10 int y; // 0 char opt; //我们协议上是支持 // request_t() = default;//生成默认的构造函数 }; //请求协议 int main() { request_t req={10,20,'*'}; //现在我们有了这个结构化的数据,所以我们就需要把它进行序列化 Json::Value root;//可以承转任意对象,json是一种kv式的序列化方案 //要序列化的对象先装到一个value对象里面 root["datax"]=req.x; root["datay"]=req.y; root["operator"]=req.opt; //FastWriter StyledWriter 有这两种类型,这是一整行没有分层的 // Json::StyledWriter writer;//我们定义了一个json里面Writer类,writer对象,这个writer式一种分层的 Json::FastWriter writer;//我们定义了一个json里面Writer类,writer对象,这个writer式一种分层的 //而FastWriter 就是一种正常的字符串样子 string json_string=writer.write(root);//这里的返回值是一个string类型的对象 //现在我们就完成了一个序列化的过程 cout< 网络在线计算器 S o c k . h p p Sock.hpp Sock.hpp
#include#include using namespace std; #include #include #include #include #include #include #include //这些静态的套接字都是属于类而不属于对象 class Sock { public: static int Socket() { int sockfd=socket(AF_INET,SOCK_STREAM,0); if(sockfd<0) { perror("socket"); exit(2); } return sockfd; } static void Listen(int sockfd) { if(listen(sockfd,5)<0) { perror("listen"); exit(4); } } static void Bind(int sockfd,uint16_t port) { struct sockaddr_in local; memset(&local,0,sizeof(local)); local.sin_family=AF_INET; local.sin_addr.s_addr=INADDR_ANY; local.sin_port=htons(port); if(bind(sockfd,(struct sockaddr*)&local,sizeof(local))<0) { perror("bind"); exit(3); } } static int Accept(int sockfd) { struct sockaddr_in peer; bzero(&peer,0); socklen_t len=sizeof(peer); int fd=accept(sockfd,(struct sockaddr*)&peer,&len); if(fd<0) { perror("accept"); exit(5); } return fd; } static void Connect(int sockfd,string ip,int port) { struct sockaddr_in svr; memset(&svr,0,sizeof(svr)); svr.sin_family=AF_INET; svr.sin_port=htons(port); svr.sin_addr.s_addr=inet_addr(ip.c_str()); if(connect(sockfd,(struct sockaddr*)&svr,sizeof(svr))<0) { perror("connect"); exit(6); } } }; p r o t o c o l . h p p protocol.hpp protocol.hpp
//我们在通信的时候要自己定制协议 //客户端和服务器要进行计算器的功能,我们要有请求有响应 //这个本质上是一个应用层网络服务 #pragma once #include#include #include #include using namespace std; //定制协议的过程,目前就是定制结构化数据的过程 //请求格式 //但是向这种,如果面对的是老客户端,一旦有一个字节没有办法发送过来,就出现了错误 //我们需要序列化这个东西 struct request_t { int x; // 10 int y; // 0 char opt; //我们协议上是支持 // request_t() = default; }; //请求协议 //这里我们写一个响应格式 struct response_t { int code; //程序运算完毕的计算状态,code=0(success),code=-1(: ),code=-2(%0),先检测code,得到result才有意义 int result; //计算结果,能否区分是正常的计算结果,还是异常退出结果 }; //这里的话我们实现一个序列化请求的函数 request_t ---->string string ReqSerialize(const request_t &req) { Json::Value root; root["one"] = req.x; root["two"] = req.y; root["operator"] = req.opt; Json::FastWriter writer; string sendwriter = writer.write(root);//调用write之后,就实现了序列化 return sendwriter; //返回序列化之后的字符串 } //这里实现一个反序列化的函数 // string--->request_t void ReqReSerialize(const string &jsonstring, request_t &req) { Json::Reader reader; Json::Value root; reader.parse(jsonstring, root); //解析进行反序列化 req.x = root["one"].asInt(); req.y = root["two"].asInt(); req.opt =(char) root["operator"].asUInt(); } //序列化响应的函数 response_t ----->string string RespSerialize(const response_t &resp) { Json::Value root; root["code"]=resp.code; root["result"]=resp.result; Json::FastWriter writer; string sendwriter = writer.write(root); return sendwriter; //返回序列化之后的字符串 } //这里实现一个反序列化响应的函数 // string--->response_t void RespReSerialize(const string &jsonstring, response_t &resp) { Json::Reader reader; Json::Value root; reader.parse(jsonstring, root); //解析进行反序列化 resp.code = root["code"].asInt(); resp.result = root["result"].asInt(); } c l i e n t . c c client.cc client.cc
#include "protocol.hpp" #include "Sock.hpp" int main(int argc, char *argv[]) { if (argc != 3) { cout << "ip+port" << endl; exit(1); } uint16_t port = atoi(argv[2]); int sockfd = Sock::Socket(); Sock::Connect(sockfd, argv[1], port); request_t req; cout << "Please Enter Data One# "; cin >> req.x; cout << "Please Enter Data Two# "; cin >> req.y; cout << "Please Enter Data Opt# "; cin >> req.opt; string sendwriter=ReqSerialize(req); write(sockfd, sendwriter.c_str(), sendwriter.size()); //这样就序列化成功了 //读取信息 char buf[1024]; ssize_t s = read(sockfd, buf, sizeof(buf) - 1); //对resp进行反序列化 response_t resp; if (s > 0) { buf[s] = 0; string msg = buf; // cout<exit(1); } return 0; } s e r v e r . c c server.cc server.cc
#include "protocol.hpp" #include "Sock.hpp" void *HandlerRequest(void *args) { pthread_detach(pthread_self()); int sockfd = *(int *)args; delete args; //业务逻辑,先读先要放序列化,然后计算,判断结果是否正确,正确返回,不正确异常 //做一个短服务,request -> 分析处理 ->构建response ->sent(response)--->close(sock) // verson1:没有明显的序列化和反序列化的过程 // 1.读取请求,但是这样的操作对于90%的情况是可以满足的,但是对于一些老的服务器就不可以使用了 //直接发的话缺少了一个序列化和放序列化的过程 char buf[1024]; ssize_t s = read(sockfd, buf, sizeof(buf) - 1); if (s < 0) { cout << "error" << endl; close(sockfd); } else if (s == 0) { cout << "client quit..." << endl; close(sockfd); } else { //只要大于0就认为读取成功了 buf[s] = 0; string msg = buf; request_t req; //进行对字符串的反序列化请求 ReqReSerialize(msg, req); //读取过来要进行一个反序列化的过程 // if (s == sizeof(req)) //因为传送过来的是一个结构体,所以就是== // { //读取到了一个完整的请求,待定 // req.x,req.y,req.opt // 2.分析请求 // 3.计算结果 response_t resp = {0, 0}; //响应,这里的默认响应结果我们都给他设置为0,默认都设置为0 // 4.构建响应,并进行返回 switch (req.opt) { 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.code = -1; else resp.result = req.x / req.y; break; case '%': if (req.y == 0) resp.code = -2; else resp.result = req.x % req.y; break; default: resp.code = -3; //代表我们的请求方法异常 break; } //处理完之后就要返回响应 cout << "request " << req.x << req.opt << req.y << endl; cout<<"response "<if (argc != 2) { cout << "ip" << endl; exit(1); } uint16_t port = atoi(argv[1]); int sockfd = Sock::Socket(); Sock::Bind(sockfd, port); Sock::Listen(sockfd); while (true) { int newsockfd = Sock::Accept(sockfd); if (newsockfd < 0) { continue; } pthread_t tid; int *pram = new int(newsockfd); pthread_create(&tid, nullptr, HandlerRequest, (void *)pram); } return 0; } 我们刚刚写的cs模式的在线版本计算器,本质上是一个应用层网络服务
- 基本通信代码是我们自己写的————————(会话层:进行通信管理)
- 序列化和反序列化时我们使用组件完成的————(表示层:设备固有数据格式和网络标准数据格式的转换)
- 请求,结果格式,code含义,等约定是我们自己写的————(针对特定应用的协议)
- 业务逻辑(计算也是我们自己写的)
HTTP协议,本质上,在定位上和我们刚刚写的网络计算机,没有区别,都是应用层协议
- 网络通信
- 序列化和反序列化
- 协议细节
http协议把这3点都实现了



