【网络】应用层协议(自定义协议)(序列和反序列化)
创始人
2024-09-24 19:32:03
0

应用层协议(自定义协议)(序列和反序列化)

  • 一、引言--应用层的使用
  • 二、应用层
    • 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  #include   using func_t = std::function;  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  #include  #include  #include  #include  #include  #include  #include  #include  #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  #include  #include  #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  

使用:
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  #include  #include   // #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层模型

在这里插入图片描述
这三层不在内核中实现,是我们手动实现。

相关内容

热门资讯

华为系统不是安卓是什么,超越安... 你知道吗?最近有个话题在科技圈里可是炸开了锅,那就是华为的系统。很多人都在问,华为的系统不是安卓是什...
m6还是安卓系统,融合与创新之... 最近是不是也被手机系统的问题给绕晕了?比如,M6手机到底是用M6系统还是安卓系统呢?别急,今天就来给...
安卓语音系统叫啥名,智能生活新... 你有没有发现,手机里的语音助手越来越聪明了?它们不仅能帮你发短信、打电话,还能帮你查天气、订外卖,简...
如何下载捏脸系统安卓,安卓设备... 你有没有想过,给手机里的游戏角色换一个全新的面孔,是不是瞬间感觉整个游戏世界都生动了起来呢?没错,今...
安卓系统米家不通知,告别频繁通... 你是不是也遇到了这个问题?手机屏幕上明明显示着各种通知,可就是没有米家的消息?别急,今天就来跟你聊聊...
安卓阵营最流畅的系统,探索最流... 你有没有想过,为什么你的安卓手机有时候会卡得像蜗牛一样?别急,今天就来给你揭秘安卓阵营中最流畅的系统...
安卓手机可以刷的系统,系统兼容... 你有没有想过,你的安卓手机其实可以像换衣服一样,换上全新的系统呢?没错,这就是今天我要跟你分享的神奇...
安卓12后还有系统吗,探索未来... 你有没有发现,随着科技的飞速发展,手机系统更新换代的速度也是越来越快呢!这不,安卓12已经横空出世,...
红米系统属于安卓系统吗,深入解... 你有没有想过,手机里的那个红米系统,它到底是不是安卓系统呢?这个问题听起来可能有点儿绕,但别急,让我...
秒懂教程“微信链接牛牛房卡怎么... 牛牛是一款非常受欢迎的棋牌游戏,咨询房/卡添加微信:44346008许多玩家在游戏中会购买房卡来享受...
一分钟了解“金花链接房卡到哪里... 金花是一款非常受欢迎的棋牌游戏,咨询房/卡添加微信:44346008许多玩家在游戏中会购买房卡来享受...
终于找到“微信链接斗牛房卡充值... 斗牛是一款非常受欢迎的棋牌游戏,咨询房/卡添加微信:44346008许多玩家在游戏中会购买房卡来享受...
一分钟了解“微信金花链接房卡平... 金花是一款非常受欢迎的棋牌游戏,咨询房/卡添加微信:44346008许多玩家在游戏中会购买房卡来享受...
房卡必备教程“斗牛房间如何开启... 斗牛是一款非常受欢迎的棋牌游戏,咨询房/卡添加微信:44346008许多玩家在游戏中会购买房卡来享受...
秒懂教程“创建金花房间链接教程... 金花是一款非常受欢迎的棋牌游戏,咨询房/卡添加微信:44346008许多玩家在游戏中会购买房卡来享受...
一分钟推荐“微信群金花房卡链接... 金花是一款非常受欢迎的棋牌游戏,咨询房/卡添加微信:44346008许多玩家在游戏中会购买房卡来享受...
秒懂教程“正版金花房卡哪里有卖... 金花是一款非常受欢迎的棋牌游戏,咨询房/卡添加微信:44346008许多玩家在游戏中会购买房卡来享受...
ia实测“微信斗牛牛小程序叫什... 微信斗牛是一款非常受欢迎的棋牌游戏,咨询房/卡添加微信:44346008许多玩家在游戏中会购买房卡来...
终于找到“怎样创建微信金花链接... 金花是一款非常受欢迎的棋牌游戏,咨询房/卡添加微信:44346008许多玩家在游戏中会购买房卡来享受...
给大家讲解“微信链接牛牛群房卡... 牛牛是一款非常受欢迎的棋牌游戏,咨询房/卡添加微信:44346008许多玩家在游戏中会购买房卡来享受...