如果客户端的数据是一个结构体,这个结构体要发送给服务端。在网络传输的过程中需要将这个结构体转化成字符串,这个过程称为序列化。而数据由字符串转化成结构体再被服务端接收的过程,称为反序列化过程。
结构化的过程其实就是一种协议的体现
1.结构化的数据不利于网络的传输(为了应用层网络通信的方便)。
2.为了方便上层使用内部成员,将应用层和网络进行了解耦。
我们之前做的tcp和UDP通信没有做任何序列化与反序列化。可以json等组件来实现序列化与反序列化。
yum install -y jsoncpp-devel
安装后我们可以在/usr/include/jsoncpp/json找到安装的头文件。
json是一款实现序列化和反序列化的组件。假设我们要实现一个网络版的计算器,客户端发送与服务端发送的结构体如下:
#include
using namespace std;
typedef struct request
{
int x;
int y;
char op;
} request_t;
typedef struct response
{
int code; // code(0)表示成功,code(-1)表示除以0,code(-2)表示对0取模
int result;
} response_t;
1.首先建立Value对象,使用其来接收结构体中的值。
2.使用Writer对象的write方法,将root对象写入字符串中,即可形成一个序列化的字符串。形成的序列化字符串有我们新增的key的内容,在反序列化的时候可以拿掉。
request_t req={10,20,'*'};
Json::Value root;//可以承载任何对象,json是一种KV式的序列化方案
root["datax"]=req.x;
root["datay"]=req.y;
root["operator"]=req.op;
Json::StyledWriter writer;
string json_string=writer.write(root);
cout<<json_string<<endl;
return 0;
拿到的字符串是:
1.首先建立Value对象与Reader对象。
2.使用Reader对象的parse方法,将Value对象与字符串相关联。
3.使用Value对象的KV结构来对结构体赋值。
string json_string=R"({"datax":10,"datay":20,"operator":42})";
cout<<json_string<<endl;
Json::Value root;
Json::Reader reader;
reader.parse(json_string,root);//将root对象与要反序列化的字符串关联
request_t req;
req.x=root["datax"].asInt();
req.y=root["datay"].asInt();
req.op=(char)root["operator"].asInt();
cout<<req.x<<req.y<<req.op<<endl;
制定协议的过程,其实就是规定服务端与客户端的结构化数据的结构是什么,比如,我们规定客户端发送的数据的格式和服务端接收数据的格式:
#include
#include
using namespace std;
typedef struct request
{
int x;
int y;
char op;
} request_t;
typedef struct response
{
int code; // code(0)表示成功,code(-1)表示除以0,code(-2)表示对0取模
int result;
} response_t;
string SerializeRequest(const request_t& req)
{
Json::Value root;
Json::FastWriter writer;
root["datax"]=req.x;
root["datay"]=req.y;
root["operator"]=req.op;
string json_string=writer.write(root);
return json_string;
}
void DeserializeRequest(string& json,request_t& out)
{
Json::Value root;
Json::Reader reader;
reader.parse(json,root);
out.x=root["datax"].asInt();
out.y=root["datay"].asInt();
out.op=(char)root["operator"].asInt();
}
string SerializeResponse(const response& res)
{
Json::Value root;
Json::FastWriter writer;
root["code"]=res.code;
root["result"]=res.result;
string json_string=writer.write(root);
return json_string;
}
void DeserializeResponse(string& json,response_t& out)
{
Json::Value root;
Json::Reader reader;
reader.parse(json,root);
out.code=root["code"].asInt();
out.result=root["result"].asInt();
}
#include
#include
#include
#include
#include
#include
#include
using namespace std;
namespace ns_Sock
{
class Sock
{
private:
public:
static int Socket()
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
cerr << "创建套接字失败" << endl;
exit(-2);
}
else
{
return sock;
}
}
static int Listen(int sock)
{
if(listen(sock,5)<0)
{
cerr<<"listen error"<<endl;
exit(-3);
}
}
static int Accept(int sock)
{
struct sockaddr_in peer;
socklen_t len=sizeof(peer);
int fd=accept(sock,(struct sockaddr*)&peer,&len);
if(fd>=0)
{
return fd;
}
else
{
exit(-5);
}
}
static void Bind(int sock,uint16_t port)
{
sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(port);
local.sin_addr.s_addr=INADDR_ANY;
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
{
cerr<<"bind error"<<endl;
exit(-4);
}
}
static void Connect(int sock,string ip,uint16_t port)
{
struct sockaddr_in server;
memset(&server,0,sizeof(server));
server.sin_family=AF_INET;
server.sin_port=htons(port);
server.sin_addr.s_addr=inet_addr(ip.c_str());
if(connect(sock,(struct sockaddr*)&server,sizeof(server))==0)
{
cout<<"connect success!"<<endl;
}
else
{
cout<<"connect fail"<<endl;
exit(-5);
}
}
};
}
#include "protonol.hpp"
#include "Sock.hpp"
using namespace ns_Sock;
using namespace std;
void Usage(string proc)
{
cout << proc << " port" << endl;
}
void *HandlerRequest(void *args)
{
int sock = *(int *)args;
delete (int *)args;
pthread_detach(pthread_self());
char buffer[1024];
ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
request_t req;
response_t res = {0, 0};
buffer[s] = 0;
string str = buffer;
cout << str << endl;
DeserializeRequest(str, req);
switch (req.op)
{
case '+':
res.result = req.x + req.y;
break;
case '-':
res.result = req.x - req.y;
break;
case '*':
res.result = req.x * req.y;
break;
case '/':
if (req.y == 0)
res.code = -1;
else
res.result = req.x / req.y;
break;
case '%':
if (req.y == 0)
res.code = -2;
else
res.result = req.x % req.y;
break;
default:
res.code = -3;
}
cout << "code=" << res.code << endl;
cout << req.x << req.op << req.y << "=" << res.result << endl;
string json_string = SerializeResponse(res);
write(sock, json_string.c_str(), json_string.size());
cout << "send an answer" << json_string;
// write(sock,&res,sizeof(res));
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
return -1;
}
uint16_t port = atoi(argv[1]);
int listen_sock = Sock::Socket();
Sock::Bind(listen_sock, port);
Sock::Listen(listen_sock);
while (true)
{
int sock = Sock::Accept(listen_sock);
if (sock > 0)
{
cout << "get a new link..." << endl;
int *pram = new int(sock);
pthread_t tid;
pthread_create(&tid, nullptr, HandlerRequest, pram);
}
}
}
#include"Sock.hpp"
#include"protonol.hpp"
using namespace std;
using namespace ns_Sock;
void Usage(string proc)
{
cout<<proc<<" ip"<<" port"<<endl;
}
int main(int argc,char* argv[])
{
if(argc!=3)
{
Usage(argv[0]);
return -1;
}
int sock=Sock::Socket();
Sock::Connect(sock,argv[1],atoi(argv[2]));
request_t req;
memset(&req,0,sizeof(req));
cout<<"Please Enter Data1:#";
cin>>req.x;
cout<<"Please Enter Data2:#";
cin>>req.y;
cout<<"Please Enter op:#";
cin>>req.op;
string json_string=SerializeRequest(req);
cout<<json_string<<endl;
write(sock,json_string.c_str(),json_string.size());
response_t res;
char buffer[1024];
memset(buffer,0,sizeof(buffer));
ssize_t s=read(sock,&buffer,sizeof(buffer)-1);
buffer[s]=0;
string str=buffer;
cout<<str<<endl;
DeserializeResponse(str,res);
cout<<"code[0:-3]#"<<res.code<<endl;
cout<<"result#"<<res.result<<endl;
}
}
我们可以将上面的代码分为三部分,分别是基本通信代码(会话层),序列化反序列化代码(表示层),约定以及业务逻辑代码(应用层)。这些在TCP/IP四层协议中统称为应用层。http协议是应用层协议,因此需要完成以上三个工作。