网络基础(二)——序列化与反序列化

目录

1、应用层

2、再谈“协议”

3、网络版计算器

Socket.hpp

TcpServer.hpp

ServerCal.hpp

ServerCal.cc

Protocol.hpp

ClientCal.cc

Log.hpp

Makefile


1、应用层

我们程序员写的一个个解决我们实际问题,满足我们日常需求的网络程序,都是在应用层。

2、再谈“协议”

协议是一种 " 约定 "。 socket api 的接口 , 在读写数据时 , 都是按 " 字符串 " 的方式来发送接收的 . 如果我们要传输一些 "结构化的数据 " 怎么办呢 ?
这个时候就要进行序列化和反序列化,什么意思呢?如图:
左边的客户端在给右边的服务器想发送这样一个msg的结构化的数据,那么服务端就必须也要有一个完全相同的结构可以接受数据,然后在客户端向网络发送数据时会先转换成字符串,然后服务器接收时再将字符串转换成结构化的数据,所以只要确保一端发送的数据,另一端能够识别,这种约定,就是应用层的协议。

3、网络版计算器

Socket.hpp

这是一个封装的套接字接口,可以创建套接字,进行绑定、监听、接收、连接以及关闭套接字。

//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.hpp"enum{SocketErr = 2,BindErr,ListenErr,
};//TODO
const int backlog = 10;class Sock
{
public:Sock(){}~Sock(){}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, "bind error, %s: %d", strerror(errno), errno);exit(ListenErr);}}int Accept(std::string *clientip, 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);return -1;}char ipstr[64];inet_ntop(AF_INET, &peer.sin_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));int n = connect(sockfd_, (struct sockaddr*)&peer, sizeof(peer));if (n == -1) {std::cerr << "connect to" << ip << ":" << port << std::endl;return false;}return true;}void Close(){close(sockfd_);}int Fd(){return sockfd_;}
private:int sockfd_;
};

TcpServer.hpp

下面是一个创建tcp服务器的接口,实现了启动服务器的功能,在里面调用其他接口处理收到的数据然后写回给客户端。

//TcpServer.hpp
#pragma once
#include <functional>
#include <string>
#include "Log.hpp"
#include "Socket.hpp"
#include <signal.h>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 InitServer()
{listensock_.Socket();listensock_.Bind(port_);listensock_.Listen();lg(Info, "init server ... done");return true;
}
void Start()
{signal(SIGCHLD, SIG_IGN);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.c_str(), clientport);//提供服务if(fork() == 0){listensock_.Close();std::string inbuffer_stream;// 数据计算while(true){char buffer[128];ssize_t n = read(sockfd, buffer, sizeof(buffer));if (n > 0){buffer[n] = 0;inbuffer_stream += buffer;lg(Debug, "debug: %s", inbuffer_stream.c_str());std::string info = callback_(inbuffer_stream);if(info.empty()) continue;write(sockfd, info.c_str(), info.size());}else if(n == 0) break;else break;}exit(0);}close(sockfd);}
}
~TcpServer()
{}
private:uint16_t port_;Sock listensock_;func_t callback_;
};

ServerCal.hpp

这是计算器服务器的接口,里面定义了计算器的基本功能加减乘除取模,调用了Protocol接口中的序列化反序列化来将数据标准化。

//ServerCal.hpp
#pragma once 
#include <iostream>
#include "Protocol.hpp"enum{Div_Zero = 1,Mod_Zero, 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.code = Div_Zero;else resp.result = req.x / req.y;}break;case '%':{if (req.y == 0) resp.code = Mod_Zero;else resp.result = req.x % req.y;}break;default:resp.code = Other_Oper;break;}return resp;}// "len"\n"10 + 20"\nstd::string Calculator(std::string &package){std::string content;bool r = Decode(package,&content);  // "len"\n"10 + 20"\nif(!r) return "";// "10 + 20"Request req;r = req.Deserialize(content);// "10 + 20" -> x=10 op=+ y=20if(!r) return "";content = ""; //Response resp = CalculatorHelper(req); resp.Serialize(&content);content = Encode(content);return content;}~ServerCal(){}
};

ServerCal.cc

这里是服务器启动的程序,通过绑定端口号启动服务器。

//ServerCal.cc
#include "TcpServer.hpp"
#include "ServerCal.hpp"static void Usage(const std::string &proc)
{std::cout << "\nUsage: " << proc << "port\n" << std::endl;
}//./servercal 8080
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(0);}uint16_t port = std::stoi(argv[1]);ServerCal cal;TcpServer *tsvp = new TcpServer(8080, std::bind(&ServerCal::Calculator, &cal, std::placeholders::_1));tsvp->InitServer();tsvp->Start();return 0;
}

Protocol.hpp

这是对数据进行序列化反序列化的接口。

//Protocol.hpp
#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"\n"x op y"
bool Decode(std::string &package, std::string *content)
{std::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);// package = len_str + content + 2std::size_t total_len = len_str.size() + len + 2;if(package.size() < total_len) return false;*content = package.substr(pos+1, len);// earse 移除报文 package.erase(0, total_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(){}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;return true;
#else Json::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+1);if(left + 2 != right) return false;op = in[left + 1];x = std::stoi(part_x);y = std::stoi(part_y);return true;
#else Json::Value root;Json::Reader r;r.parse(in, root);x = root["x"].asInt();y = root["y"].asInt();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(){}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;
#else Json::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) // "result cpde"{
#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(pos+1);result = std::stoi(part_left);code = std::stoi(part_right);return true;
#else Json::Value root;Json::Reader r;r.parse(in, root);result = root["result"].asInt();code = root["code"].asInt();return true;
#endif}void DebugPrint(){std::cout << "结果响应完成: result:" << result << ", code:" << code << std::endl;}
public:int result;int code; // 0, 可信,否则!0具体是几,表明对应的错误原因
};

ClientCal.cc

这是客户端启动的程序,通过服务器ip和端口连接服务器。

//ClientCal.cc
#include <iostream>
#include <string>
#include <ctime>
#include <assert.h>
#include <unistd.h>
#include "Socket.hpp"
#include "Protocol.hpp"static void 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(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(sockfd.Fd(), package.c_str(), package.size());char buffer[128];ssize_t n = read(sockfd.Fd(), buffer, sizeof(buffer)); //我们也无法保证能读到一个完整的报文if(n > 0){buffer[n] = 0;inbuffer_stream += buffer; //"len"\n"result code"\nstd::string content;bool r = Decode(inbuffer_stream, &content); // "result code"assert(r);Response resp;r = resp.Deserialize(content);assert(r);resp.DebugPrint();}sleep(1);}sockfd.Close();return 0;
}

Log.hpp

这是日志接口。

//Log.hpp
#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int method){printMethod = method;}std::string levelToString(int level){switch (level){case Info:return "Info";case Debug:return "Debug";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "None";}}// void logmessage(int level, const char *format, ...)// {//     time_t t = time(nullptr);//     struct tm *ctime = localtime(&t);//     char leftbuffer[SIZE];//     snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),//              ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,//              ctime->tm_hour, ctime->tm_min, ctime->tm_sec);//     // va_list s;//     // va_start(s, format);//     char rightbuffer[SIZE];//     vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);//     // va_end(s);//     // 格式:默认部分+自定义部分//     char logtxt[SIZE * 2];//     snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);//     // printf("%s", logtxt); // 暂时打印//     printLog(level, logtxt);// }void printLog(int level, const std::string &logtxt){switch (printMethod){case Screen:std::cout << logtxt << std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string &logname, const std::string &logtxt){std::string _logname = path + logname;int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // "log.txt"if (fd < 0)return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string &logtxt){std::string filename = LogFile;filename += ".";filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"printOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式:默认部分+自定义部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);// printf("%s", logtxt); // 暂时打印printLog(level, logtxt);}private:int printMethod;std::string path;
};Log lg;// int sum(int n, ...)
// {
//     va_list s; // char*
//     va_start(s, n);//     int sum = 0;
//     while(n)
//     {
//         sum += va_arg(s, int); // printf("hello %d, hello %s, hello %c, hello %d,", 1, "hello", 'c', 123);
//         n--;
//     }//     va_end(s); //s = NULL
//     return sum;
// }

Makefile

//Makefile
.PHONY:all
all:servercal clientcalFlag=-DMySelf=1
Lib=-ljsoncppservercal:ServerCal.ccg++ -o $@ $^ -std=c++11 $(Lib) #$(Flag)
clientcal:ClientCal.ccg++ -o $@ $^ -std=c++11 -g $(Lib) #$(Flag).PHONY:clean
clean:rm -f servercal clientcal

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/292726.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QT+Opencv+yolov5实现监测

功能说明&#xff1a;使用QTOpencvyolov5实现监测 仓库链接&#xff1a;https://gitee.com/wangyoujie11/qt_yolov5.git git本仓库到本地 一、环境配置 1.opencv配置 将OpenCV-MinGW-Build-OpenCV-4.5.2-x64文件夹放在自己的一个目录下&#xff0c;如我的路径&#xff1a; …

appium辅助自动化工具-- Appium studio

这里我要给大家介绍一款appium辅助自动化测试工具appium studio&#xff0c;你没看错&#xff0c;不是android studio&#xff0c;也不是appium android studio&#xff0c;就是appium studio&#xff01; 下载地址&#xff1a; Appium Studio | Digital.ai Continuous Test…

智慧公厕的技术融合策略

智慧公厕是迎合现代城市发展需要的一项重要基础设施&#xff0c;其设计的技术融合策略在实现公共厕所泛在感知、互通互联、协同构筑智慧城市等方面起到了关键作用。本文将以智慧公厕源头实力厂家广州中期科技有限公司&#xff0c;大量精品案例现场实景实图实例&#xff0c;从物…

Makefile:动态库的编译链接与使用(六)

1、动态链接库 动态链接库&#xff1a;不会把代码编译到二进制文件中&#xff0c;而是运行时才去加载&#xff0c;所以只需要维护一个地址 动态&#xff1a;运行时才去加载&#xff0c;即所谓的动态加载连接&#xff1a;指库文件和二进制程序分离&#xff0c;用某种特殊的手段…

MySQL - 高阶语句(一)

先准备一张表 create table class1 (id int,name varchar(10) primary key not null ,score decimal(5,2),address varchar(20),hobbid int(5));insert into class1 values(1,liuyi,80,beijing,2); insert into class1 values(2,wangwu,90,shengzheng,2); insert into class1 …

图腾柱PFC:HP1010为您的电动两轮车之旅提供绿色,高效,安全的动力

电动两轮车不仅为当今生活提供了便利&#xff0c;更是一种健康和绿色的出行方式。想象一下&#xff0c;在经过一整晚的充分休息&#xff0c;骑上爱车&#xff0c;满血复活的准备开始新的一天。您会愿意带着如何给心爱的两轮车充电的担心开始这一天吗&#xff1f; 随着越来越…

第1章.提示词:开启AI智慧之门的钥匙

什么是提示词&#xff1f; 提示词&#xff0c;是引导语言模型的指令&#xff0c;让用户能够驾驭模型的输出&#xff0c;确保生成的文本符合需求。 ChatGPT&#xff0c;这位文字界的艺术大师&#xff0c;以transformer架构为基石&#xff0c;能轻松驾驭海量数据&#xff0c;编织…

JUC并发编程(七)

1、不可变对象 1.1、概念 不可变类是指一旦创建对象实例后&#xff0c;就不能修改该实例的状态。这意味着不可变类的对象是不可修改的&#xff0c;其内部状态在对象创建后不能被更改。不可变类通常具有以下特征&#xff1a; 实例状态不可改变&#xff1a;一旦不可变类的对象被…

中药知识分享

中药知识分享 声明&#xff1a;本文根据《懒兔子》公众号公益视频整理&#xff0c;参考网络信息略有改动&#xff0c;图片来源于网络&#xff0c;如有侵权&#xff0c;请联系删除。文章内容如有不正确之处请指正批评。仅供学习使用。请勿擅自服用本文提及的药材&#xff0c;否…

http响应练习—在服务器端渲染html(SSR)

一、什么是服务器端渲染&#xff08;SSR&#xff09; 简单说&#xff0c;就是在服务器上把网页生成好&#xff0c;整个的HTML页面生成出来&#xff0c;生成出的页面已经包含了所有必要的数据和结构信息&#xff0c;然后直接发给浏览器进行展现。 二、例题 要求搭建http服务&a…

pymysql使用记录

最近由于需要来学习一下pymysql。 先来认识一下pymysql&#xff1a; PyMySQL 是 Python 中一个用于连接 MySQL 数据库的库。它允许 Python 程序通过简单的 API 调用来连接、操作和管理 MySQL 数据库。PyMySQL 是在 Python 中使用纯 Python 编写的&#xff0c;因此它可以在几…

2024/3/31周报

文章目录 摘要Abstract文献阅读题目创新点实验数据研究区域数据和材料 方法XGBoost algorithmLong Short‑Term Memory AlgorithmEvaluation of the Model Accuracy 实验结果 深度学习XGBoost代码实现AdaBoostBoostingAdaBoost算法AdaBoost代码实现 总结 摘要 本周阅读了一篇基…

Mamba: Linear-Time Sequence Modeling with Selective State Spaces(论文笔记)

What can I say? 2024年我还能说什么&#xff1f; Mamba out! 曼巴出来了&#xff01; 原文链接&#xff1a; [2312.00752] Mamba: Linear-Time Sequence Modeling with Selective State Spaces (arxiv.org) 原文笔记&#xff1a; What&#xff1a; Mamba: Linear-Time …

NFC RC522开发记录

文章目录 一、ID卡、IC卡(M1卡、CPU卡)的区别二、RC522读写操作1. 数据读写流程三、RC522驱动代码1. RC522 与 STM32 的接线图2. RC522.c3. RC522.h4. main.c一、ID卡、IC卡(M1卡、CPU卡)的区别 ID卡 :只存储了ID号,设备识别ID号,没有算法可言,容易复制,安全性低IC卡包含了…

Unity-C#进阶——3.27更新中

文章目录 数据结构类ArrayListStackQueueHashtable 泛型泛型类、泛型方法、泛型接口ListDictionaryLinkedList泛型栈&#xff0c;泛型队列 委托和事件委托事件匿名函数Lambad 表达式**闭包** List 排序逆变协变多线程进程线程多线程方法&#xff1a;线程之间共享数据&#xff1…

洛谷_P2437 蜜蜂路线_python写法_高精度加法

目录 1. 40分代码 2.高精度加法 3.全AC代码 4.惊掉下巴的解法 P2437 蜜蜂路线 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 1. 40分代码 m, n map(int,input().split())ans 0 d [1,2] flag [0 for _ in range(n1)] def fun(step):global ansif step n:ans 1return…

订单系统-RPC快速入门

RPC快速入门 概述 关于rpc&#xff0c;只需要知道他是一种协议&#xff0c;项目之间能够远程调用函数。 快速入门 我们前边下载好的两个包&#xff0c;在idea中打开之后&#xff0c;我们创建这么几个文件夹。 至于是干什么的&#xff0c;以后细说。创建好之后我们在produc…

数据结构--单链表(c语言实现)

一.单链表的设计 1.单链表的结构定义: typedef struct Node{int data;//数据域struct Node* next;//后继指针 }Node,*List; 2.单链表的设计示意图: 3.注意,单链表的最后一个节点的next域为NULL; 4.为什么要有一个头节点?(简单方便,不用传二级指针); 二.单链表的实现 //初始化 …

SQL中的UNION和UNION ALL

SQL中的UNION和UNION ALL是用来合并两个或更多SELECT语句结果集的运算符。它们的主要区别在于是否去除重复行以及是否执行排序操作。 UNION&#xff1a; - UNION操作符用于合并两个或多个查询结果集&#xff0c;形成一个新的结果集。 - 它会自动删除结果集中的重复行&#xf…

C语言操作符详细讲解

前言 本次博客一定会让刚刚学习C语言小白有所收获 本次操作符讲解不仅分类还会有代码示例 好好看 好好学 花上几分钟就可以避免许多坑 1 操作符的基本使用 1.1操作符的分类 按功能分 算术操作符&#xff1a; 、- 、* 、/ 、% 移位操作符: >> << 位操作符…