应用层协议(自定义协议)(序列和反序列化)
- 一、引言--应用层的使用
- 二、应用层
- 1、网络版本计算器
- (1)协议定制和序列反序列化
- (2)网络版计算器协议定制
- i、封装有效载荷(默认上面图是Request的,下面图是Response的)
- ii、封装报头(单独出来的函数)
- iii、分离有效载荷(默认上面图是Request的,下面图是Response的)
- iv、解析报文(单独出来的函数)
- v、测试结果
- (3)计算器函数
- (4)计算器与网络连接(网络服务代码)
- (5)附加:Socket封装
- (6)服务端代码
- (7)测试结果
- 测试结果Debug
- 解决方法:每一次都删掉规定好的报文
- (8)客户端编写
- (9)客户端多个请求同时发送
- 2、json(用来自动支持序列反序列化)
- 3、用json进行序列反序列化结果
- 三、重谈OSI7层模型
一、引言–应用层的使用
二、应用层
协议是一种 “约定”. socket api的接口, 在读写数据时, 都是按 “字符串” 的方式来发送接收的. 如果我们要传输一些"结构化的数据" 怎么办呢?
1、网络版本计算器
(1)协议定制和序列反序列化
结构化数据,所有主机都要是一样的结构体类型(协议的定制),再通过网络的序列和反序列化方便数据的收发。
(2)网络版计算器协议定制
i、封装有效载荷(默认上面图是Request的,下面图是Response的)
其实就是将这个结构体转化成为"x op y"!和"result code"!
ii、封装报头(单独出来的函数)
“len”\n"s"
iii、分离有效载荷(默认上面图是Request的,下面图是Response的)
将string s 进行分离出来,分离成整型或者char类型的。
iv、解析报文(单独出来的函数)
v、测试结果
(3)计算器函数
CalHelper函数是进行完加减乘除取模后返回结果的函数,Calculator是将计算结果进行封装完报头的函数。
Response CalHelper(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.code = DIVERRO;
}
else
{
resp.result = req.x / req.y;
}
break;
}
case '%':
{
if (req.y == 0)
{
resp.code = MOERROR;
}
else
{
resp.result = req.x % req.y;
}
break;
}
default:
resp.code = OTHERERROR;
break;
}
return resp;
}
// "len"\n"10 + 20"\n
std::string Calculator(std::string &package)
{
std::string content;
bool r = Decode(package, &content); // 分离出有效载荷 // "len"\n"10 + 20"\n
if (!r)
{
return "";
}
// 反序列化 -- 分离开每一个元素
Request req;
r = req.Deserialize(content); // x=10 op=+ y=20
if (!r)
{
return "";
}
// 处理结果
content = "";
Response resp = CalHelper(req); // result=30 code=0
// 序列化
resp.Serialize(&content); // "30 0"
// 封装报头
content = Encode(content); // "len"\n"30 0"\n
return content;
}
(4)计算器与网络连接(网络服务代码)
代码逻辑为:InitTcpServer函数是用来进行监听套接字的设置绑定和监听的,RunTcpServer函数是用来将底层的监听套接字拿到上层来使用并进行提供服务的。
#pragma once
#include "Log.hpp"
#include "Socket.hpp"
#include <signal.h>
#include <functional>
using func_t = std::function<std::string(std::string &package)>;
class TcpServer
{
public:
TcpServer(uint16_t port, func_t callback)
: _port(port), _callback(callback)
{
}
bool InitTcpServer()
{
_listensocketfd.Socket();
_listensocketfd.Bind(_port);
_listensocketfd.Listen();
lg(Info, "init servercal...");
return true;
}
void RunTcpServer()
{
signal(SIGCHLD, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
while (true)
{
std::string clientip;
uint16_t clientport;
int socketfd = _listensocketfd.Accept(&clientip, &clientport);
if (socketfd < 0)
{
continue;
}
lg(Info, "accept a new link..., sockfd:%d, clientip:%s, clientport:%d", socketfd, clientip.c_str(), clientport);
// 提供服务
// pid_t id = fork();
if (fork() == 0)
{
_listensocketfd.Close();
std::string inbuffer_stream;
// 数据计算
while (true)
{
char buffer[128];
ssize_t n = read(socketfd, buffer, sizeof(buffer));
if (n > 0)
{
buffer[n] = 0;
inbuffer_stream += buffer;
lg(Debug, "debug:%s", inbuffer_stream.c_str());
// while (true)
//{
std::string info = _callback(inbuffer_stream);
if (info.empty())
continue;
// break;
// lg(Debug, "debug, response:\n%s", info.c_str());
// lg(Debug, "debug:\n%s", inbuffer_stream.c_str());
write(socketfd, info.c_str(), info.size());
//}
}
else if (n == 0)
{
break;
}
else
break;
}
exit(0);
}
close(socketfd);
}
}
~TcpServer()
{
}
private:
Sock _listensocketfd;
uint16_t _port;
func_t _callback;
};
回调函数示意:
(5)附加:Socket封装
#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstring>
#include <string>
#include "Log.hpp"
//extern Log lg;
enum
{
SOCKETERR = 2,
BINDERR,
LISTENERR
};
const int backlog = 10;
class Sock
{
public:
Sock()
{
}
~Sock()
{
}
public:
void Socket()
{
_socketfd = socket(AF_INET, SOCK_STREAM, 0);
if (_socketfd < 0)
{
lg(Fatal, "socket create err, %d:%s", errno, strerror(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(_socketfd, (struct sockaddr *)&local, sizeof(local)) < 0)
{
lg(Fatal, "bind error, %d:%s", errno, strerror(errno));
exit(BINDERR);
}
}
void Listen()
{
if (listen(_socketfd, backlog) < 0)
{
lg(Fatal, "listen error, %d:%s", errno, strerror(errno));
exit(LISTENERR);
}
}
int Accept(std::string *clientip, uint16_t *clientport)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int newfd = accept(_socketfd, (struct sockaddr *)&peer, &len);
if (newfd < 0)
{
lg(Warning, "accept error, %d:%s", errno, strerror(errno));
return -1;
}
char ipstr[64];
inet_ntop(AF_INET, &(peer.sin_addr), ipstr, sizeof(ipstr));
*clientip = ipstr;
*clientport = ntohs(peer.sin_port);
return newfd;
}
int Connect()
{
return 0;
}
void Close()
{
close(_socketfd);
}
private:
int _socketfd;
};
(6)服务端代码
#include "TcpServer.hpp"
#include "Protocol.hpp"
#include "Servercal.hpp"
static void Usage(const std::string &proc)
{
std::cout << "\nUsage" << proc << " port\n\n" << std::endl;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(0);
}
uint16_t clientport = std::stoi(argv[1]);
ServerCal cal;
TcpServer *tsvp = new TcpServer(clientport, std::bind(&ServerCal::Calculator, &cal, std::placeholders::_1));
tsvp->InitTcpServer();
tsvp->RunTcpServer();
return 0;
}
(7)测试结果
测试结果Debug
解决方法:每一次都删掉规定好的报文
(8)客户端编写
Socket.hpp:
ClientCal.cc:
#include <iostream>
#include <unistd.h>
#include <cassert>
#include "Socket.hpp"
#include "Protocol.hpp"
static void Usage(const std::string &proc)
{
std::cout << "\nUsage" << proc << " serverip: serverport\n\n"
<< std::endl;
}
// ./clientcal 127.0.0.1 8888
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
exit(0);
}
uint16_t serverport = std::stoi(argv[2]);
std::string serverip = argv[1];
Sock socketfd;
socketfd.Socket();
bool r = socketfd.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(1234);
int y = rand() % 100;
usleep(4321);
char oper = opers[rand() % opers.size()];
Request req(x, y, oper);
req.DebugPrint();
std::string package;
req.Serialize(&package);
package = Encode(package);
std::cout << "这是新的发出去的请求:\n" << package;
write(socketfd.Fd(), package.c_str(), package.size());
char buffer[128];
ssize_t n = read(socketfd.Fd(), buffer, sizeof(buffer)); // 保证不了读到的是一个完整的报文
if (n > 0)
{
buffer[n] = 0;
inbuffer_stream += buffer;
std::string content;
bool rqs = Decode(inbuffer_stream, &content);
assert(rqs);
Response resp;
rqs = resp.Deserialize(content);
assert(rqs);
resp.DebugPrint();
}
std::cout << "=================================================" << std::endl;
sleep(1);
cnt++;
}
socketfd.Close();
return 0;
}
测试结果:
(9)客户端多个请求同时发送
2、json(用来自动支持序列反序列化)
安装命令:sudo yum install -y jsoncpp-devel
安装成功:
头文件:
#include <jsoncpp/json/json.h>
使用:
FastWriter:
测试用例一(序列化):
int main()
{
Json::Value part1;
part1["haha"] = "haha";
part1["hehe"] = "hehe";
Json::Value root;
root["x"] = 100;
root["y"] = 200;
root["op"] = '+';
root["desc"] = "this is a + oper";
root["test"] = part1;
Json::FastWriter w;
std::string res = w.write(root);
std::cout << res << std::endl;
return 0;
}
编译:
g++ test.cc -ljsoncpp
测试用例二(反序列化):
FastWriter:
int main()
{
Json::Value part1;
part1["haha"] = "haha";
part1["hehe"] = "hehe";
Json::Value root;
root["x"] = 100;
root["y"] = 200;
root["op"] = '+';
root["desc"] = "this is a + oper";
root["test"] = part1;
Json::FastWriter w;
std::string res = w.write(root);
sleep(3);
Json::Value v;
Json::Reader r;
r.parse(res, v);
int x = v["x"].asInt();
int y = v["y"].asInt();
char op = v["op"].asInt();
std::string desc = v["desc"].asString();
Json::Value temp = v["test"];
std::cout << x << std::endl;
std::cout << y << std::endl;
std::cout << op << std::endl;
std::cout << desc << std::endl;
return 0;
}
测试用例三(序列化):
StyleWriter:
int main()
{
Json::Value part1;
part1["haha"] = "haha";
part1["hehe"] = "hehe";
Json::Value root;
root["x"] = 100;
root["y"] = 200;
root["op"] = '+';
root["desc"] = "this is a + oper";
root["test"] = part1;
Json::StyleWriter w;
std::string res = w.write(root);
std::cout << res << std::endl;
return 0;
}
3、用json进行序列反序列化结果
Makefile:
.PHONY:all
all:servercal clientcal
Flag=-DMySelf=1
Lib=-ljsoncpp
servercal:ServerCalculator.cc
g++ -o $@ $^ -std=c++11 $(Lib) #$(Flag)
clientcal:ClientCalculator.cc
g++ -o $@ $^ -std=c++11 $(Lib) #$(Flag)
.PHONY:clean
clean:
rm -f servercal clientcal
Protocol.hpp:
#pragma once
// 定制协议
#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>
// #define MySelf 1
const 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;
}
bool Decode(std::string &package, std::string *content) // "len"\n"x op y"\n
{
// package = len_str + content_str + 2 总长度等于长度字符串长度+正文长度+两个\n的长度
size_t pos = package.find(protocol_sep);
if (pos == std::string::npos)
{
return false;
}
std::string len_str = package.substr(0, pos);
// 分理出长度
size_t len = std::stoi(len_str);
size_t total_len = len_str.size() + len + 2;
if (package.size() < total_len)
{
return false;
}
// 完整报文--提取有效载荷
*content = package.substr(pos + 1, 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)
{
#ifdef MySelf
// 构建有效载荷
// struct --> string "x op y" => "len"\n"x op y"
std::string s = std::to_string(x);
s += blank_space_sep;
s += op;
s += blank_space_sep;
s += std::to_string(y);
*out = s;
#else
Json::Value root;
root["x"] = x;
root["y"] = y;
root["op"] = op;
Json::FastWriter w;
*out = w.write(root);
#endif
return true;
}
bool Deserialize(const std::string &in) // "x op y"
{
#ifdef MySelf
size_t leftpos = in.find(blank_space_sep);
if (leftpos == std::string::npos)
{
return false;
}
std::string part_x = in.substr(0, leftpos);
size_t rightpos = in.rfind(blank_space_sep);
if (rightpos == std::string::npos)
{
return false;
}
std::string part_y = in.substr(rightpos + 1);
if (leftpos + 1 != rightpos - 1)
{
return false;
}
op = in[leftpos + 1];
x = std::stoi(part_x);
y = std::stoi(part_y);
#else
Json::Value root;
Json::Reader r;
r.parse(in, root);
x = root["x"].asInt();
y = root["y"].asInt();
op = root["op"].asInt();
#endif
return true;
}
void DebugPrint()
{
std::cout << "新请求构建完成:" << x << op << y << "=?" << std::endl;
}
public:
int 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
// 构建有效载荷
// "len"\n"result code"
std::string s = std::to_string(result);
s += blank_space_sep;
s += std::to_string(code);
*out = s;
#else
Json::Value root;
root["result"] = result;
root["code"] = code;
Json::FastWriter w;
*out = w.write(root);
#endif
return true;
}
bool Deserialize(const std::string &in) // "result code"
{
#ifdef MySelf
size_t pos = in.find(blank_space_sep);
if (pos == std::string::npos)
{
return false;
}
std::string res = in.substr(0, pos);
std::string cod = in.substr(pos + 1);
result = std::stoi(res);
code = std::stoi(cod);
#else
Json::Value root;
Json::Reader r;
r.parse(in, root);
result = root["result"].asInt();
code = root["code"].asInt();
#endif
return true;
}
void DebugPrint()
{
std::cout << "结果响应完成, 结果是:" << "result:" << result << " " << "code:" << code << std::endl;
}
public:
int result;
int code; // 0表示可信,非零表示不可信,相对应的数字表示相对应的错误原因
};
三、重谈OSI7层模型
这三层不在内核中实现,是我们手动实现。