网络版本计算器

目录

  • 网络版本计算器
    • 1.1 TcpServer.hpp
    • 1.2 ServerCal.hpp
    • 1.3 ServerCal.cc
    • 1.4 Protocol.hpp
    • 1.5 Socket.hpp
    • 1.6 makefile
    • 1.7 ClientCal.cc
    • 1.8 log.hpp

网络版本计算器

1.1 TcpServer.hpp

#pragma once#include "Protocol.hpp"
#include "Socket.hpp"
#include <functional>
#include "log.hpp"
#include <signal.h>using func_t = function<string(string &)>;class TcpServer
{
public:TcpServer(const uint16_t &port, func_t callback): _port(port), _callback(callback){}~TcpServer(){}bool InitServer(){// 创建套接字_listenSock.Socket();int sockfd = _listenSock.Sockfd();//设置端口复用int opt = 1;setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));// 绑定_listenSock.Bind(_port);// 监听_listenSock.Listen();log(Info, "Init server successed...");return true;}void Start(){// 守护进程化//参数中的第一个0表示更改该进程的路径为根目录执行,第二个0表示//把0,1,2三个文件描述符输出的东西重定向到 /dev/null 文件中daemon(0,0);// 屏蔽一些信号signal(SIGPIPE, SIG_IGN);signal(SIGCHLD, SIG_IGN);while (true){string clientip;uint16_t clientport;int sockfd = _listenSock.Accept(clientip, clientport);if (sockfd > 0){if (fork() == 0){// 子进程继承了父进程的监听套接字,但是并用不上,所以可以close掉_listenSock.Close();// 提供服务string inbuffer_stream;while (true){char tmp[10240];bzero(tmp, sizeof(tmp));ssize_t n = read(sockfd, tmp, sizeof(tmp) - 1);if (n > 0){tmp[n] = '\0';inbuffer_stream += tmp;cout<<"=========================="<<endl;cout<<inbuffer_stream<<endl;cout<<"=========================="<<endl;string ret;while (true){//只要返回的不是一个空字符串,就说明请求还没有//处理完,那么就继续处理剩下的请求,把处理结果//存在ret中string str = _callback(inbuffer_stream);if (str.empty()){break;}ret+=str;}//把请求发送给客户端write(sockfd, ret.c_str(), ret.size());}else if (n == 0){log(Info, "client exit...");break;}else{log(Fatal, "read failed,errno:%d,errstring:%s", errno, strerror(errno));break;}}close(sockfd);exit(0);}close(sockfd);}}}private:Sock _listenSock;uint16_t _port;func_t _callback;
};

1.2 ServerCal.hpp

#pragma once#include <iostream>
#include "Protocol.hpp"enum
{DivZeroErr,ModZeroErr,OtherErr
};class ServerCal
{
public:ServerCal(){}~ServerCal(){}Response CalculatorHelper(const Request &req){Response resp(0, 0);int x = req._x;int y = req._y;switch (req._op){case '+':{resp._result = x + y;break;}case '-':{resp._result = x - y;break;}case '*':{resp._result = x * y;break;}case '/':{if (y == 0){resp._code = DivZeroErr;break;}resp._result = x / y;break;}case '%':{if (y == 0){resp._code = ModZeroErr;break;}resp._result = x % y;break;}default:{resp._code = OtherErr;break;}}return resp;}//package: "len"\n"1 + 1"\nstring Calculator(string& package){string content;//解码bool ret=Decode(package,content);if(!ret){return "";}cout<<"///"<<endl;cout<<package<<endl;cout<<"///"<<endl;Request req;//反序列化ret=req.Request_DeSerialize(content);if(!ret){return "";}cout<<"get a new task:";printf("%d %c %d\n",req._x,req._op,req._y);//传入请求,返回响应Response resp=CalculatorHelper(req);//序列化string resp_str=resp.Response_Serialize();//添加报头Encode(resp_str);return resp_str;}
};

1.3 ServerCal.cc

#include <iostream>
using namespace std;
#include "ServerCal.hpp"
#include "Protocol.hpp"
#include "TcpServer.hpp"
#include <memory>// int main()
// {
//     Request req(145,20,'%');
//     string ret=req.Request_Serialize();
//     Encode(ret);
//     cout<<ret<<endl;//     cout<<endl;
//     Request r;
//     r.Request_DeSerialize(ret);
//     cout<<r._x<<endl;
//     cout<<r._op<<endl;
//     cout<<r._y<<endl;//     string content;
//     Decode(ret,content);
//     cout<<content<<endl;//     // Response resp(10,0);
//     // string ret=resp.Response_Serialize();
//     // Encode(ret);
//     // cout<<ret<<endl;//     // string content;
//     // Decode(ret,content);
//     // cout<<content<<endl;//     // Response r;
//     // r.Response_DeSerialize(content);
//     // cout<<r._result<<endl;
//     // cout<<r._code<<endl;//     return 0;
// }void Usage(string proc)
{cout<<"\n\t";cout<<"Usage: "<<proc<<" serverPort"<<endl<<endl;
}//Usage: ./servercal
int main(int argc,char* argv[])
{if(argc!=2){Usage(argv[0]);exit(1);}uint16_t serverPort=(uint16_t)stoi(argv[1]);ServerCal cal;//服务器绑定的是Calculator函数,该函数有两个参数,一个绑定为Calculator的this指针,另一个参数自己传unique_ptr<TcpServer> svr(new TcpServer(serverPort,bind(&ServerCal::Calculator,&cal,placeholders::_1)));//初始化svr->InitServer();//启动svr->Start();return 0;
}

1.4 Protocol.hpp

#pragma once
#include <iostream>
using namespace std;
#include "log.hpp"
#include <jsoncpp/json/json.h>const static string blank_space_sep = " ";
const static string protocol_sep = "\n";// #define MySelfextern Log log;// 添加报头和分隔符
void Encode(string &content)
{size_t len = content.size();content = to_string(len) + protocol_sep + content + protocol_sep;
}// 去掉报头,如果解码成功,则继续移除报文,否则,直接返回false
bool Decode(string &package, string &content)
{size_t pos = package.find(protocol_sep);// 找不到\n不一定是报文的格式错误,也有可能是报文不完整导致的,所以这里// 无需打错误日志信息if (pos == string::npos){// log(Warning, "this package format is failed,package:%s", package.c_str());return false;}string len_str = package.substr(0, pos);size_t len = stoi(len_str);size_t total_len = len_str.size() + len + 2;if (package.size() < total_len){log(Warning, "this package format is failed,package:%s", package.c_str());return false;}content = package.substr(pos + 1, len);// 解码成功则移除报文package.erase(0, total_len);return true;
}class Request
{
public:Request(){}Request(int data1, int data2, char oper): _x(data1), _y(data2), _op(oper){}~Request(){}// 序列化,即把结构化的数据序列化成字符串// 格式:"len"\n"x [+-*/%] y"\nstring Request_Serialize(){
#ifdef MySelfstring content;content += to_string(_x);content += blank_space_sep;content += _op;content += blank_space_sep;content += to_string(_y);return content;
#else//用第三方库jsoncpp进行序列化//Json::Value是一个key:value的万能对象Json::Value root;root["x"] = _x;root["op"] = _op;root["y"] = _y;Json::FastWriter w;string content = w.write(root);return content;
#endif}// 反序列化bool Request_DeSerialize(const string &content_str){
#ifdef MySelfsize_t left = content_str.find(blank_space_sep);if (left == string::npos){log(Warning, "this content_str format is failed,content_str:%s", content_str.c_str());return false;}//取出content_str的前半部分string left_str = content_str.substr(0, left);size_t right = content_str.rfind(blank_space_sep);if (right == string::npos){log(Warning, "this content_str format is failed,content_str:%s", content_str.c_str());return false;}//取出content_str的后半部分string right_str = content_str.substr(right + 1);if (left + 2 != right){log(Warning, "this content_str format is failed,content_str:%s", content_str.c_str());return false;}_x = stoi(left_str);_y = stoi(right_str);_op = content_str[left + 1];return true;
#else//万能对象Json::Value v;Json::Reader r;//把字符串形式的content_str变成结构化的数据,并写到万能对象中去r.parse(content_str, v);_x = v["x"].asInt();_op = v["op"].asInt();_y = v["y"].asInt();return true;
#endif}public:int _x;int _y;char _op;
};class Response
{
public:Response(){}Response(const int &result, const int &code = 0): _result(result), _code(code){}~Response(){}// 格式:"len"\n"result code"\nstring Response_Serialize(){
#ifdef MySelfstring package;string result_str = to_string(_result);string code_str = to_string(_code);size_t body_len = result_str.size() + code_str.size();package += result_str;package += blank_space_sep;package += code_str;return package;
#else// 万能对象Json::Value root;root["result"] = _result;root["code"] = _code;Json::FastWriter w;string package = w.write(root);return package;
#endif}bool Response_DeSerialize(const string &content_str){
#ifdef MySelfsize_t content_left = content_str.find(blank_space_sep);if (content_left == string::npos){log(Warning, "this content_str format is failed,content_str:%s", content_str.c_str());return false;}string content_left_str = content_str.substr(0, content_left);string content_right_str = content_str.substr(content_left + 1);_result = stoi(content_left_str);_code = stoi(content_right_str);return true;
#else//Json::Value v是万能对象Json::Value v;Json::Reader r;r.parse(content_str,v);_result=v["result"].asInt();_code=v["code"].asInt();return true;
#endif}public:int _result;int _code = 0;
};

1.5 Socket.hpp

#pragma once#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "log.hpp"
#include <unistd.h>
#include <strings.h>
#include <cstring>
#include <string>int backlog = 10;enum
{SockErr = 2,BindErr,ListenErr,ConnectErr,
};class Sock
{
public:Sock(): _sockfd(-1){}~Sock(){if(_sockfd>0){close(_sockfd);}}// 创建套接字void Socket(){_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0){log(Fatal, "socket failed,errno:%d,errstring:%s", errno, strerror(errno));exit(SockErr);}log(Info, "socket successed, sockfd:%d", _sockfd);}// 绑定void Bind(const uint16_t &serverPort){struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(serverPort);local.sin_addr.s_addr = INADDR_ANY;if (bind(_sockfd, (struct sockaddr *)(&local), sizeof(local)) < 0){log(Fatal, "bind failed,errno:%d,errstring:%s", errno, strerror(errno));exit(BindErr);}log(Info, "bind successed...");}// 监听void Listen(){if (listen(_sockfd, backlog) < 0){log(Fatal, "set listen state failed,errno:%d,errstring:%s", errno, strerror(errno));exit(ListenErr);}log(Info, "set listen state successed");}//获取连接int Accept(string& clientip,uint16_t& clientport){struct sockaddr_in client;socklen_t len=sizeof(client);bzero(&client,sizeof(client));int sockfd=accept(_sockfd,(struct sockaddr*)(&client),&len);if(sockfd<0){log(Warning, "accept new link failed,errno:%d,errstring:%s", errno, strerror(errno));return -1;}log(Info,"accept a new link...,sockfd:%d",sockfd);clientip=inet_ntoa(client.sin_addr);clientport=(uint16_t)(ntohs(client.sin_port));return sockfd;}// 连接void Connect(const string &serverIp, const uint16_t &serverPort){struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(serverIp.c_str());server.sin_port = htons(serverPort);if (connect(_sockfd, (struct sockaddr *)(&server), sizeof(server)) < 0){log(Fatal, "connect server failed,errno:%d,errstring:%s", errno, strerror(errno));exit(ConnectErr);}log(Info, "connect server succeeded...");}void Close(){if(_sockfd>0){close(_sockfd);}}int Sockfd(){return _sockfd;}private:int _sockfd;
};

1.6 makefile

.PHONY:all
all:servercal clientcalservercal:ServerCal.ccg++ -o $@ $^ -std=c++11 -ljsoncpp
clientcal:ClientCal.ccg++ -o $@ $^ -std=c++11 -ljsoncpp.PHONY:clean
clean:rm -f clientcal servercal

1.7 ClientCal.cc

#include <iostream>
using namespace std;
#include "Socket.hpp"
#include "Protocol.hpp"
#include <ctime>void Usage(string proc)
{cout << "\n\t";cout << "Usage: " << proc << " serverip serverPort" << endl<< endl;
}// Usage: ./servercal
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}srand(time(nullptr));string serverIp = argv[1];uint16_t serverPort = (uint16_t)stoi(argv[2]);// 创建套接字Sock sock;sock.Socket();// 连接sock.Connect(serverIp, serverPort);string oper = "+-*/%&^$@#";int sockfd = sock.Sockfd();string package;while (true){int x = rand() % 100 + 1;int y = rand() % 100;char op = oper[rand() % oper.size()];//构建请求Request req(x, y, op);//序列化string req_str = req.Request_Serialize();cout << "req:" << req_str << endl;//添加报头Encode(req_str);cout << "这是最新的需要发到网络上去的报文:" << endl;cout << req_str << endl;write(sockfd, req_str.c_str(), req_str.size());char buffer[10240];bzero(buffer, sizeof(buffer));int n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = '\0';package += buffer;while (true){string content;Response resp;//解码bool ret = Decode(package, content);if(!ret){//如果解码失败,说明package出问题了或者package中的//内容不是一个完整的报文,跳出本层循环break;}//来到这里说明解码成功cout << content << endl;//反序列化resp.Response_DeSerialize(content);cout << "len : " << content.size() << endl;cout << "resp._result : " << resp._result << endl;cout << "resp._code : " << resp._code << endl;}}else if (n == 0){cout << "server quit..." << endl;break;}else{perror("read failed");break;}sleep(10);}sock.Close();return 0;
}

1.8 log.hpp

#pragma once#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string>
#include <time.h>
#include <stdarg.h>// 日志等级
#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 SIZE 1024#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int mothod){printMethod = mothod;}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 printlog(int level,const string& logtxt){switch(printMethod){case Screen:{cout<<logtxt<<endl;break;}case OneFile:{PrintOneFile(LogFile,logtxt);break;}case Classfile:{PrintClassfile(level,logtxt);break;}default:{break;}}}void PrintOneFile(const string& logname,const string& logtxt){string _logname=path+logname;int fd=open(_logname.c_str(),O_WRONLY|O_CREAT|O_APPEND,0666);if(fd<0){perror("open fail");return;}write(fd,logtxt.c_str(),logtxt.size());close(fd);}void PrintClassfile(int level,const string& logtxt){string filename=LogFile;filename+='.';filename+=LevelToString(level);PrintOneFile(filename,logtxt);}void operator()(int level,const char* format,...){time_t t=time(nullptr);struct tm* ctime=localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer,SIZE,"[%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]={0};vsnprintf(rightbuffer,SIZE,format,s);va_end(s);char logtxt[SIZE*2];snprintf(logtxt,sizeof(logtxt),"%s %s",leftbuffer,rightbuffer);printlog(level,logtxt);}~Log(){}private:// 打印方法int printMethod;string path;
};//定义一个全局的log
Log log;

以上就是关于网络版本计算器的全部实现过程了,你学会了吗?如果感觉到有所收获的话,就点点赞点点关注呗,后期还会持续更新网络编程的相关知识哦,我们下期见!!!

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

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

相关文章

单片机串口 奇偶校验 配置问题

一、问题描述 使用GD32单片机串口进行通信测试&#xff0c;单片机的串口配置的是偶校验(Even)、数据位为8、停止位为1、波特率为9600。串口测试软件用的格西烽火&#xff0c;软件的配置如下&#xff1a;   单片机通过串口和串口测试软件进行通信交互&#xff0c;软件收到的数…

时钟算法---模运算、乘法逆元

使用时钟讲解了模运算和乘法逆元 1.加法 2.减法 3. 乘法 4. 除法&#xff08;乘法逆元&#xff09; 5.乘方 6. 对数 乘方的逆运算称为对数。在一般的数学中&#xff0c;求对数并不难&#xff0c;例如&#xff1a;

记一次页面接口502问题:“502 Bad Gateway”

接收别人的项目进行迭代&#xff0c;项目部署到服务器上之后&#xff0c;有一个接口数据刷不出来&#xff0c;一直502 后来联想到网关的问题&#xff0c;想通过设置白名单的方式解决&#xff0c;设置之后依旧不行。 查看nginx日志发现报错&#xff1a; *169 connect() failed …

ELFK日志采 - QuickStart

文章目录 架构选型ELKEFLK ElasticsearchES集群搭建常用命令 Filebeat功能介绍安装步骤Filebeat配置详解filebeat常用命令 Logstash功能介绍安装步骤Input插件Filter插件Grok Filter 插件Mutate Filter 插件常见的插件配置选项&#xff1a;Mutate Filter配置案例&#xff1a; O…

2024.1.27力扣每日一题——最大合金数

2024.1.27 题目来源我的题解方法一 二分查找 题目来源 力扣每日一题&#xff1b;题序&#xff1a;2861 我的题解 方法一 二分查找 使用二分查找&#xff0c;下界为0&#xff0c;上界因为预算和已有金属最大上限是 1 0 8 10^8 108&#xff0c;所以设置二分查找上界为2* 1 0 8…

Leetcode24:两两交换链表中的节点

一、题目 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例&#xff1a; 输入&#xff1a;head [1,2,3,4] 输出&#xff…

亲测解决vscode的debug用不了、点了没反应

这个问题在小虎登录vscode同步了设置后出现,原因是launch文件被修改或删除。解决方法是重新添加launch。 坏境配置 win11 + vscode 解决方法 Ctrl + shift + P,搜索debug添加配置: 选择python debugger。 结果生成了一个文件在当前路径: launch内容: {// Use Int…

centos安装inpanel

前置条件 安装python yum -y install python 安装 cd /usr/local git clone https://gitee.com/WangZhe168_admin/inpanel.git cd inpanel python install.py 安装过程需要设置账户 密码 端口号 我设置的是admin:admin 10050 使用 打开浏览器,输入 http://192.168.168.…

AI助力农作物自动采摘,基于YOLOv7【tiny/l/x】不同系列参数模型开发构建作物生产场景下番茄采摘检测计数分析系统

去年十一那会无意间刷到一个视频展示的就是德国机械收割机非常高效自动化地24小时不间断地在超广阔的土地上采摘各种作物&#xff0c;专家设计出来了很多用于采摘不同农作物的大型机械&#xff0c;看着非常震撼&#xff0c;但是我们国内农业的发展还是相对比较滞后的&#xff0…

YOLO-World——超级轻量级开放词汇目标检测方法

前言 目标检测一直是计算机视觉领域中不可忽视的基础挑战&#xff0c;对图像理解、机器人技术和自主驾驶等领域具有广泛应用。随着深度神经网络的发展&#xff0c;目标检测方面的研究取得了显著进展。尽管这些方法取得了成功&#xff0c;但它们存在一些限制&#xff0c;主要体…

如何让MySQL从部署到稳定运行?

如何让MySQL从部署到稳定运行&#xff1f; 1. 安装MySQL 8保姆级教程 2. 《从菜鸟到大师之路 MySQL 篇》 3. 关于MySQL的66个问题 4. MySQL 的学习资源史上最全 5. 掌握 SQL 这些核心知识点&#xff0c;出去吹牛逼再也不担心了

Spring Web Header 解析常见错误

在上一章&#xff0c;我们梳理了 URL 相关错误。实际上&#xff0c;对于一个 HTTP 请求而言&#xff0c;URL 固然重要&#xff0c;但是为了便于用户使用&#xff0c;URL 的长度有限&#xff0c;所能携带的信息也因此受到了制约。 如果想提供更多的信息&#xff0c;Header 往往…

Redis(三)(实战篇)

查漏补缺 1.spring 事务失效 有时候我们需要在某个 Service 类的某个方法中&#xff0c;调用另外一个事务方法&#xff0c;比如&#xff1a; Service public class UserService {Autowiredprivate UserMapper userMapper;public void add(UserModel userModel) {userMapper.…

@Valid常用的用法

一、通用用法 除了Null&#xff0c; NotNull&#xff0c; NotBlank&#xff0c;NotEmpty 这四个外&#xff0c;其他所有的注解&#xff0c;传 null 时都会被当作有效处理。 注解验证的数据类型备注Null任意类型参数值必须是 NullNotNull任意类型参数值必须不是 NullNotBlank只…

MySQL:关于存储过程

1、删除已存在存储过程 DROP PROCEDUCRE IF EXISTS P_NAME 2、创建存储过程 CREATE PROCEDUCRE P_NAME(NAME VARCHAR(36) ) 3、存储过程业务逻辑 BEGIN DECLARE NAME INT;BEGINEND; END 4、存储过程给变量赋值 SET 赋固定值, INTO配合查询语句赋值【多个值使用id,name i…

Antd+React+react-resizable实现表格拖拽功能

1、先看效果 2、环境准备 "dependencies": {"antd": "^5.4.0","react-resizable": "^3.0.4",},"devDependencies": {"types/react": "^18.0.33","types/react-resizable": "^…

Linux中有名管道和无名管道

无名管道基础 进程间通信介绍 常用通信方式 无名管道&#xff08;pipe&#xff09; 有名管道 &#xff08;fifo&#xff09; 信号&#xff08;signal&#xff09; 共享内存(mmap) 套接字&#xff08;socket&#xff09;过时的IPC通信方式 System V IPC 共享内存&#xff08;sh…

数据结构——A/复杂度

A/基础铺垫 1. 什么是数据结构&#xff1f; 数据结构(Data Structure)是计算机存储、组织数据的方式&#xff0c;指相互之间存在一种或多种特定关系的 数据元素的集合。 2.什么是算法&#xff1f; 算法(Algorithm):就是定义良好的计算过程&#xff0c;他取一个或一组的值为输…

Windows下Node.js下载安装及环境变量配置教程

Windows下Node.js下载安装及环境变量配置教程 安装版本&#xff1a;node-v18.19.0-x64.msi 文章目录 Windows下Node.js下载安装及环境变量配置教程一、Node.js和NPM简介二、下载地址三、安装步骤四、环境配置五、安装淘宝镜像总结 一、Node.js和NPM简介 1、Node.js &#xf…

前端架构: 脚手架在前端研发流程中的意义

关于脚手架 脚手架 command-line interface基于文本界面&#xff0c;通过中断输入命令执行常见的脚手架&#xff1a;npm, webpack-cli, vue-cli拿 npm 这个脚手架来说 在终端当中输入 npm 命令, 系统就会通过文本方式返回 npm 的使用方法它这种通过命令行执行的操作方式效率很…