当前位置: 首页 > news >正文

网络版本计算器

目录

  • 网络版本计算器
    • 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;

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

相关文章:

  • C语言:月份缩写
  • 游戏后端如何实现服务器之间的负载均衡?
  • 2024-2-4-复习作业
  • 车位检测,YOLOV8,OPENCV调用
  • Java排序算法-持续更新中
  • OpenCV 图像处理六(傅里叶变换、模板匹配与霍夫变换)
  • MySQL操作问题汇总
  • 视频业务像素、带宽、存储空间计算
  • SpringBoot集成Redisson实现限流(二)
  • QCustomplot实现灰度曲线图
  • 大型语言模型(LLM)的优势、劣势和风险
  • 计算机毕业设计 基于SpringBoot的线上教育培训办公系统的设计与实现 Java实战项目 附源码+文档+视频讲解
  • Elasticsearch:将文档级安全性 (DLS) 添加到你的内部知识搜索
  • 【前端web入门第四天】02 CSS三大特性+背景图
  • 【安卓跨程序共享数据,探究ContentProvider】
  • 「面试题」如何实现一个圣杯布局?
  • 【划重点】MySQL技术内幕:InnoDB存储引擎
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • Android系统模拟器绘制实现概述
  • codis proxy处理流程
  • JavaScript学习总结——原型
  • laravel5.5 视图共享数据
  • Python 反序列化安全问题(二)
  • React的组件模式
  • spark本地环境的搭建到运行第一个spark程序
  • Vue 动态创建 component
  • 机器学习学习笔记一
  • 机器学习中为什么要做归一化normalization
  • 微信小程序:实现悬浮返回和分享按钮
  • 小程序01:wepy框架整合iview webapp UI
  • 在Mac OS X上安装 Ruby运行环境
  • 主流的CSS水平和垂直居中技术大全
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • ​渐进式Web应用PWA的未来
  • ​水经微图Web1.5.0版即将上线
  • (非本人原创)史记·柴静列传(r4笔记第65天)
  • (分布式缓存)Redis哨兵
  • (附源码)spring boot校园拼车微信小程序 毕业设计 091617
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • ..回顾17,展望18
  • .NET/C# 解压 Zip 文件时出现异常:System.IO.InvalidDataException: 找不到中央目录结尾记录。
  • .NET/C# 在 64 位进程中读取 32 位进程重定向后的注册表
  • .NET企业级应用架构设计系列之应用服务器
  • @angular/cli项目构建--http(2)
  • @Data注解的作用
  • @Mapper作用
  • @SentinelResource详解
  • @SpringBootApplication 包含的三个注解及其含义
  • @开发者,一文搞懂什么是 C# 计时器!
  • [ Algorithm ] N次方算法 N Square 动态规划解决
  • [2009][note]构成理想导体超材料的有源THz欺骗表面等离子激元开关——
  • [Android View] 可绘制形状 (Shape Xml)
  • [AUTOSAR][诊断管理][ECU][$37] 请求退出传输。终止数据传输的(上传/下载)
  • [C#] 如何调用Python脚本程序