网络版本计算器

网络版本计算器

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"\n
    string 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 MySelf

extern 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"\n
    string Request_Serialize()
    {
#ifdef MySelf
        string 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 MySelf
        size_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"\n
    string Response_Serialize()
    {
#ifdef MySelf
        string 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 MySelf
        size_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 clientcal

servercal:ServerCal.cc
	g++ -o $@ $^ -std=c++11 -ljsoncpp
clientcal:ClientCal.cc
	g++ -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;

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

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值