Linux笔记---网络计算器

1. 网络程序分层

我们说过,OSI7层模型十分完美,但是因特网实际上采用的是TCP/IP五层模型:

实际上,对比可以发现,TCP/IP模型实际上就是将OSI的前三层模型合并为了应用层。

这就提示我们,我们设计的应用程序应当存在三个层次:

  • 应用层:人机交互层,应用程序可以访问网络服务。
  • 表示层:确保数据采用可用形式,并且是数据加密的形式。
  • 会话层:维护连接,并负责控制端口和会话。

简单来说,在服务器端,这三层就是提供实际服务的顶层、规定数据传输协议的协议层、负责建立连接与断开连接的服务器层。

#include <pthread.h>
#include "Socket.hpp"
#include "Server.hpp"
#include "Protocol.hpp"
#include "Calculator.hpp"
#include "Daemon.hpp"
using namespace SocketModule;void Usage(const std::string &name)
{std::cout << "usage: " << name << " + port" << std::endl;
}int main(int argc, char* args[])
{if (argc != 2){Usage(args[0]);exit(USAGE_ERROR);}in_port_t port = std::stoi(args[1]);// 日志切换为文件策略USE_FILE_STRATEGY();// 设置为守护进程if(Daemon(0, 0) == -1){LOG(LogLevel::FATAL) << "Deamon: 设置守护进程失败! " << strerror(errno);exit(DAEMON_ERROR);}// daemon(0, 0)// 顶层, 计算器Calculator calculator;// 协议层Protocol protocol([&calculator](const Request& request){return calculator.Calculate(request);});// 服务器层Server server(port, [&protocol](const std::shared_ptr<TCPConnectSocket>& socket){protocol.ServerService(socket);});server.Run();return 0;
}

2. 协议

为简单起见,我们定制如下的协议:

  • 服务器端只处理类似" x oper y "的简单表达式,所以客户端用于发起请求的结构化数据只包含三个变量:" int x "、" int y "、" char oper "
  • 服务器端处理完表达式后需要返回结果与状态码,所以服务器端用于返回响应的结构化数据需要包含两个变量:" int result "、" int status "
  • 数据在网络上的传输格式为JSON串
  • 一个完整的请求/响应报文包含四个部分:JSON串的长度 + sep + JSON串 + sep。其中sep = " \n\r "

于是就有下面的协议类(Protocol.hpp):

#pragma once
#include <jsoncpp/json/json.h>
#include "Socket.hpp"
using namespace SocketModule;class Request
{
public:Request(){}Request(int x, int y, char oper){serialize(x, y, oper);}Request(const std::string& json_str){if(!deserialize(json_str))LOG(LogLevel::ERROR) << "Response: 反序列化失败! ";}// 序列化std::string serialize(int x, int y, char oper){Json::Value root;root["x"] = _x = x;root["y"] = _y = y;root["oper"] = _oper = oper;Json::FastWriter writer;_json_str = writer.write(root);return _json_str;}// 反序列化bool deserialize(const std::string& json_ptr){Json::Value root;Json::Reader reader;if(reader.parse(json_ptr, root)){_json_str = json_ptr;_x = root["x"].asInt();_y = root["y"].asInt();_oper = root["oper"].asInt();return true;}return false;}int X() const {return _x;}int Y() const {return _y;}int Oper() const {return _oper;}std::string JsonStr() const {return _json_str;}
private:int _x, _y;char _oper;std::string _json_str;
};class Response
{
public:Response(){}Response(int result, int status){serialize(result, status);}Response(const std::string& json_str){if(!deserialize(json_str))LOG(LogLevel::ERROR) << "Response: 反序列化失败! ";}// 序列化std::string serialize(int result, int status){Json::Value root;root["result"] = _result = result;root["status"] = _status = status;Json::FastWriter writer;_json_str = writer.write(root);return _json_str;}// 反序列化bool deserialize(const std::string& json_str){Json::Value root;Json::Reader reader;if(reader.parse(json_str, root)){_json_str = json_str;_result = root["result"].asInt();_status = root["status"].asInt();return true;}return false;}int Result() const {return _result;}int Status() const {return _status;}std::string JsonStr() const {return _json_str;}
private:int _result, _status;std::string _json_str;
};using request_handler = std::function<Response(const Request&)>;
class Protocol
{
public:Protocol(request_handler handler): _handler(handler){}// 封装// package = size + esp + json_ptr + espvoid encapsulate(const std::string& json_str, std::string& package){int size = json_str.size();package = std::to_string(size) + esp + json_str + esp;}// 解封装bool decapsulate(std::string& package, std::string& json_str){auto pos = package.find(esp);if(pos == std::string::npos){return false;}std::string size_str = package.substr(0, pos);int size = std::stoi(size_str.c_str());int total_len = size_str.size() + 2 * esp.size() + size;if(package.size() >= total_len){json_str = package.substr(pos + esp.size(), size);package.erase(0, total_len);return true;}return false;}void ClientService(){// todo}// 服务器端服务void ServerService(const std::shared_ptr<TCPConnectSocket>& socket){std::string package, buffer, json_str, send_msg;while(socket->Receive(buffer)){package += buffer;if(decapsulate(package, json_str)){Request request(json_str);// 使用顶层提供的回调函数来处理请求Response response = _handler(request);encapsulate(response.JsonStr(), send_msg);socket->Send(send_msg);}}LOG(LogLevel::INFO) << "[" << socket->addr().Info() << "]连接已断开! ";}
private:static const std::string esp;// 顶层提供的用于处理请求的回调方法request_handler _handler;
};
const std::string Protocol::esp = "/n/r";

3. 项目代码

3.1 Calculator.hpp

#pragma once
#include "Protocol.hpp"
#include "Log.hpp"
using namespace LogModule;class Calculator
{
public:Response Calculate(const Request &request){int result = 0, status = 0;int x = request.X(), y = request.Y();char oper = request.Oper();switch (oper){case '+':result = x + y;break;case '-':result = x - y;break;case '*':result = x * y;break;case '/':if(y == 0){LOG(LogLevel::ERROR) << "Calculator: 除零错误! ";status = 1;}elseresult = x / y;break;case '%':if(y == 0){LOG(LogLevel::ERROR) << "Calculator: 模零错误! ";status = 2;}elseresult = x % y;break;case '^':result = x ^ y;break;case '&':result = x & y;break;case '|':result = x | y;break;default:LOG(LogLevel::ERROR) << "Calculator: 未知类型的运算符[" << oper << "]";status = 2;break;}Response response(result, status);return response;}
};

当然,这个项目只是用作对套接字编程的学习,因此协议的定制与顶层服务的实现都较为简单。

3.2 Server.hpp

#pragma once
#include "Socket.hpp"
#include "Common.hpp"
using namespace SocketModule;
using func_t = std::function<void(const std::shared_ptr<TCPConnectSocket>&)>;class Server : public NoCopy
{
public:Server(in_port_t port, func_t service): _lis_socket(port), _service(service){}void Run(){while(true){auto socket = _lis_socket.Accept();int id = fork();if(id < 0){LOG(LogLevel::FATAL) << "fork: 创建子进程失败! " << strerror(errno);throw std::runtime_error("fork failed");}else if( id == 0){_lis_socket.Close();// 子进程if(fork() > 0) exit(NORMAL);// 孙子进程_service(socket);exit(NORMAL);}else{// 父进程socket->Close();}}}~Server(){_lis_socket.Close();}
private:TCPListenSocket _lis_socket;func_t _service;
};

3.3 Server.cpp

#include <pthread.h>
#include "Socket.hpp"
#include "Server.hpp"
#include "Protocol.hpp"
#include "Calculator.hpp"
#include "Daemon.hpp"
using namespace SocketModule;void Usage(const std::string &name)
{std::cout << "usage: " << name << " + port" << std::endl;
}int main(int argc, char* args[])
{if (argc != 2){Usage(args[0]);exit(USAGE_ERROR);}in_port_t port = std::stoi(args[1]);// 日志切换为文件策略USE_FILE_STRATEGY();// 设置为守护进程if(Daemon(0, 0) == -1){LOG(LogLevel::FATAL) << "Deamon: 设置守护进程失败! " << strerror(errno);exit(DAEMON_ERROR);}// daemon(0, 0)// 顶层Calculator calculator;// 协议层Protocol protocol([&calculator](const Request& request){return calculator.Calculate(request);});// 服务器层Server server(port, [&protocol](const std::shared_ptr<TCPConnectSocket>& socket){protocol.ServerService(socket);});server.Run();return 0;
}

3.4 Client.cpp

#include "Socket.hpp"
#include "Protocol.hpp"
using namespace SocketModule;void Usage(const std::string &name)
{std::cout << "usage: " << name << " + ip" << " + port" << std::endl;
}int main(int argc, char* args[])
{if (argc != 3){Usage(args[0]);exit(USAGE_ERROR);}in_port_t port = std::stoi(args[2]);TCPClientSocket cli_socket(args[1], port);int x, y;char oper;Request request;Response response;Protocol protocol([](const Request&){return Response();});std::string send_msg, recv_msg, json_str;InetAddr server = cli_socket.addr();while(true){std::cout << "Enter expression(x oper y)# ";std::cin >> x >> oper >> y;request.serialize(x, y, oper);protocol.encapsulate(request.JsonStr(), send_msg);cli_socket.Send(send_msg);std::string tmp;while(!protocol.decapsulate(recv_msg, json_str)){cli_socket.Receive(tmp);recv_msg += tmp;}response.deserialize(json_str);std::cout << "Recive from Client# " << response.Result() << "[" << response.Status() << "]" << std::endl;}return 0;
}

3.5 Common.hpp

#pragma once
#include <functional>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include "InetAddr.hpp"
using namespace LogModule;enum SocketExitCode
{NORMAL = 0,SOCKET_ERROR,BIND_ERROR,LISTEN_ERROR,FORK_ERROR,READ_ERROR,WRITE_ERROR,USAGE_ERROR,CONNECT_ERROR,DAEMON_ERROR
};class NoCopy
{
public:NoCopy(){}NoCopy(const NoCopy&) = delete;NoCopy& operator=(const NoCopy&) = delete;
};

3.6 Socket.hpp

https://blog.csdn.net/2302_80372340/article/details/151293752?spm=1011.2415.3001.5331

4. 演示

日志内容(/var/log/NetCal.log):

关闭服务器:

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

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

相关文章

《智能网联汽车交通仿真软件可信度评估》团标启动会圆满举办

让数据真正闭环的L4级自动驾驶仿真工具链&#xff0d;杭州千岑智能科技有限公司&#xff1a;RSim 近日&#xff0c;由中国仿真学会主办、清华大学牵头的《智能网联汽车交通仿真软件可信度评估》团体标准启动会在北京成功举行。杭州千岑科技有限公司作为智能网联汽车测试验证领域…

关于 MCU 芯片外围电路的快速入门介绍

MCU&#xff08;微控制单元&#xff0c;Microcontroller Unit&#xff09;是嵌入式系统的“大脑”&#xff0c;但需通过外围电路实现供电、信号输入/ 输出、通信、存储等功能&#xff0c;才能构成完整的工作系统。外围电路的设计直接决定 MCU 的稳定性、功能扩展性和适用场景&a…

Uniapp onLoad 和 onShow 区别

一、核心区别生命周期触发时机执行次数参数获取onLoad页面首次创建时触发仅1次支持获取URL参数optionsonShow页面每次显示时触发&#xff08;包括返回&#xff09;多次无法获取URL参数二、实战数据请求场景优先使用onLoad请求数据的场景&#xff1a;初始化数据当需要根据URL参数…

大模型预训练评估指标

模型效果评测 关于 Language Modeling 的量化指标&#xff0c;较为普遍的有 [PPL]&#xff0c;[BPC]等,可以简单理解为在生成结果和目标文本之间的 Cross Entropy Loss 上做了一些处理&#xff0c;这种方式可以用来评估模型对「语言模板」的拟合程度即给定一段话&#xff0c;预…

【Matlab】-- 机器学习项目 - 基于XGBoost算法的数据回归预测

文章目录 文章目录01 内容概要02 部分代码03 代码解读04 运行结果05 基于XGBoost算法的数据回归预测源码01 内容概要 XGBoost属于集成学习中的Boosting方法&#xff0c;其基本思想是&#xff1a; 逐步构建多个弱学习器&#xff08;通常是CART决策树&#xff09;&#xff0c;每…

Memory in LLM Agent

Memory in LLM Agent 1 为什么需要“记忆” —— 背景与动机 在构建 LLM Agent&#xff08;Large Language Model Agent&#xff0c;大语言模型驱动的智能体&#xff09;的过程中&#xff0c;“记忆”&#xff08;Memory&#xff09;是一个绕不开的核心问题。没有记忆的 Agent…

三甲地市级医院数据仓湖数智化建设路径与编程工具选型研究(上)

摘要 本研究旨在探索三甲地市级医院数据仓湖数智化建设的实施路径与工具选型策略,以响应国家《"十四五"全民健康信息化规划》中2025年医疗数据平台联通全覆盖的政策要求,同时解决地市级医院面临的资源限制(年均信息化投入占总营收1.5%)、区域协同需求突出及多业…

25.9.10_CTF-reverse_RC4那些事儿

CTF-reverse_RC4那些事儿 0x00 RC4加密知识点 推荐看这位up主的视频https://www.bilibili.com/video/BV1G64y1Y7p4/?spm_id_from333.1391.0.0&p2 简单来说RC4算法包括两部分KSA(利用Key生成S盒)和PRGA(利用S盒生成密钥流): KSA: 初始化S&#xff08;一般是0-255&…

网络编程(6)

【0】复习 Modbus&#xff1a;modbus tcp modbus rtu Modbus TCP: 特点&#xff1a;主从问答&#xff08;控制 采集信息&#xff09; 应用层协议&#xff08;基于TCP通信&#xff09;、默认端口502 组成&#xff1a;报文头&#xff08;7 事物2 协议2 长度2 单元表示1&#xff…

技术文章大纲:AI绘画—动漫角色生成赛

技术文章大纲&#xff1a;AI绘画—动漫角色生成赛 背景与意义 动漫角色生成赛的兴起与发展AI绘画技术在动漫创作中的应用价值比赛对推动AI艺术创新的作用 技术核心&#xff1a;AI绘画模型 主流模型介绍&#xff08;如Stable Diffusion、MidJourney、DALLE&#xff09;针对动…

Flink-新增 Kafka source 引发状态丢失导致启动失败

背景 Flink Job 新增 kafka source 算子,从状态保留并启动后提示 org.apache.flink.util.StateMigrationException: The new state typeSerializer for operator state must not be incompatible,导致任务 Fail。 Source: task-kafka-source -> task-kafka-transform (1…

【系统架构设计(26)】系统可靠性分析与设计详解:构建高可用软件系统的核心技术

文章目录一、本文知识覆盖范围1、概述2、知识体系概览二、系统可靠性基础概念1、可靠性与可用性的本质区别2、软件可靠性与硬件可靠性的深度对比3、核心可靠性指标的业务价值三、系统架构可靠性模型1、串联系统的可靠性挑战2、并联系统的高可靠性设计3、混合系统的复杂性管理四…

4 C 语言数据结构实战:栈和队列完整实现(结构体 + 函数)+ 最小栈解决方案

栈和队列 1. 栈 栈&#xff1a;⼀种特殊的线性表&#xff0c;其只允许在固定的⼀端进⾏插⼊和删除元素操作。进⾏数据插⼊和删除操作 的⼀端称为栈顶&#xff0c;另⼀端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则。 压栈&…

Milvus基于docker主机外挂实践

一、安装docker与我之前写的原博客&#xff1a;ubuntu安装milvus向量数据库&#xff0c;获取key不同&#xff0c;原博客获取key已经过时# 更新Ubuntu软件包列表和已安装软件的版本: sudo apt update# 安装Ubuntu系统的依赖包 sudo apt-get install ca-certificates curl gnupg …

使用python test测试http接口

使用python test测试http接口获取token和控制session&#xff0c;后面大多数接口要带上这些信息 import time import requestsfrom common.aes_algorithm import AES from config.config import Config from config.log import logclass Common:username "admin"pas…

平时只会CRUD,没有高质量项目经验,我该怎么办

我没有项目经验怎么办 首先&#xff0c;不管是应届生还是社招几年工作经验的朋友&#xff0c;除非特别厉害的人&#xff0c;大家都会遇到这个问题。 我们该怎么处理&#xff0c;关注hikktn&#xff01;为你解答这个问题。 问AI世面上那个大厂程序员项目推荐 为什么这么说呢&…

网编.hw.9.10

云盘下载#include <myhead.h> #define SER_IP "192.168.108.93" #define SER_PORT 69 #define addr "192.168.109.6" #define port 8888/******************主程序******************/ int main(int argc, const char *argv[]) {//1、创建一个用于通…

Java调用magic-api中post接口参数问题

Java调用magic-api中post接口参数问题magic官方文档中只提供了get写法解决方法magic官方文档中只提供了get写法 实测使用官方写法调用get接口可调通&#xff0c;参数正常获取&#xff0c;但更换为post写法后&#xff0c;magic脚本中body获取为空 Autowired MagicAPIService s…

《sklearn机器学习——管道和复合估计器》联合特征(FeatureUnion)

超详细解说 sklearn 中的联合特征&#xff08;FeatureUnion&#xff09; 1. FeatureUnion 简介 FeatureUnion 是 scikit-learn 中的一个工具&#xff0c;用于并行地组合多个特征提取器的输出。它允许你将不同的特征提取方法&#xff08;如文本向量化、数值特征缩放、自定义特征…

Eyeshot 2025.3 3D 图形工具包

Eyeshot 2025.3 现在支持 E57 格式Eyeshot 2025.3 现在支持 E57 格式&#xff0c;可直接从 3D 扫描系统导入点云、图像和元数据。Eyeshot 由 devDept 开发&#xff0c;是一款功能全面的 3D 图形工具包&#xff0c;专为构建工程和 CAD(计算机辅助设计)应用程序的 .NET 开发人员而…