自定义协议(序列化和反序列化)| 守护进程

目录

一、协议

二、序列化与反序列化

2.1、自定义序列化与反序列化

2.2、Json序列化与反序列化

三、网络计算器的简单实现

四、细节处理

五、守护进程

六、网络计算器完整代码


一、协议

        是对数据格式和计算机之间交换数据时必须遵守的规则的正式描述。简单的说了,网络中计算机要能够互相顺利的通信,就必须讲同样的语言,语言就相当于协议

        为了使数据在网络上能够从源主机到达目的主机,网络通信的参与方必须遵循相同的规则,我们将这套规则称为协议。只有使用相同的协议,主机间才能进行通信。 

二、序列化与反序列化

2.1、自定义序列化与反序列化

        今天,我们作为客户端,想要让服务端帮助我们进行计算,然后将结果返回给我们,这就是一个网络计算器了。如果我们要计算两数相加,比如 a+b,那么现在问题来了,我们应该如何将a,+,b这三个数据传输给服务器呢?是一个一个传呢,还是整体传输呢?

        如果客户端将这些结构化的数据单独一个个的发送到网络当中,那么服务端从网络当中获取这些数据时也只能一个个获取,此时服务端还需要考虑,哪个是左操作数,哪个是操作符,哪个是右操作数。所以这样不合适。

        对于一起发送。a,+,b三个数据组成一组结构化的数据,我们首先想到使用一个结构体将三个数据打包,一起发送给服务器,而我们所用的各种通信函数只允许我们传输字符串,所以我们必须要将结构化的数据先转化成字符串,然后才能发送给服务器。而服务器接收到字符串后,则必须将字符串进行解析,转成结构化的数据,进行计算,然后将结果转成字符串发回客户端。这个过程就叫做“序列化”和“反序列化”。

~ 序列化:就是将对象的状态信息转换为可以存储或传输的形式(字节序列)的过程。

~ 反序列化:就是把字节序列恢复为对象的过程。

Protocol.hpp:自定义

#pragma once
#include <iostream>
#include <string>
#include <cstdbool>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstdlib>
#include <cstring>
 
#define SPACE " "
#define SPACELEN strlen(SPACE)
 
std::string Recv(int sock)
{
    char buffer[1024];
    ssize_t s = recv(sock, buffer, sizeof buffer, 0);
    if (s == 0)
        std::cout << "客户端退出!" << std::endl;
    else if (s > 0)
        return buffer;
    else
        std::cerr << "客户端退出!" << std::endl;
 
    return "";
}
 
void Send(int sock, const std::string &str)
{
    ssize_t s = send(sock, str.c_str(), str.size(), 0);
}
 
class Request
{
public:
    std::string Serialize()
    {
        std::string requeststr = std::to_string(x_);
        requeststr += SPACE;
        requeststr += op_;
        requeststr += SPACE;
        requeststr += std::to_string(y_);
        return requeststr;
    }
 
    // x_ op_ y_
    bool Deserialization(const std::string &str)
    {
        std::size_t left = str.find(SPACE);
        if (left == std::string::npos)
            return false;
        std::size_t right = str.rfind(SPACE);
        if (right == std::string::npos)
            return false;
 
        x_ = atoi(str.substr(0, left).c_str());
        y_ = atoi(str.substr(right + SPACELEN).c_str());
        op_ = str[left + SPACELEN];
        return true;
    }
 
public:
    Request() {}
    Request(const int &x, const int &y, const char &op) : x_(x), y_(y), op_(op) {}
    ~Request() {}
 
public:
    int x_;
    int y_;
    char op_;
};
 
class Response
{
public:
    // code_ result_
    std::string Serialize()
    {
        std::string str = std::to_string(code_);
        str += SPACE;
        str += std::to_string(result_);
        return str;
    }
 
    bool Deserialization(std::string &str)
    {
        std::size_t pos = str.find(SPACE);
        if (pos == std::string::npos)
            return false;
        code_ = atoi(str.substr(0, pos).c_str());
        result_ = atoi(str.substr(pos + SPACELEN).c_str());
        return true;
    }
 
public:
    Response(const int &result = 0, const int &code = 0) : result_(result), code_(code) {}
    ~Response() {}
 
public:
    int result_;
    int code_;
};

2.2、Json序列化与反序列化

对于序列化和反序列化,上面我们自己的方案其实也很多问题,所以我们除了可以自定义方案以外,还可以使用别人的已经实现好了的方案。比如我们接下来讲到的:json。

使用前,我们需要安装json库: sudo yum install jsoncpp-devel

Protocol.hpp

#include <jsoncpp/json/json.h>
 
class Request
{
public:
    std::string Serialize()
    {
        Json::Value root;
        root["x"] = x_;
        root["y"] = y_;
        root["op"] = op_;
        Json::FastWriter writer;
        return writer.write(root);
    }
 
    // x_ op_ y_
    bool Deserialization(const std::string &str)
    {
        Json::Value root;
        Json::Reader reader;
        reader.parse(str, root);
        x_ = root["x"].asInt();
        y_ = root["y"].asInt();
        op_ = root["op"].asInt();
        return true;
    }
 
public:
    Request() {}
    Request(const int &x, const int &y, const char &op) : x_(x), y_(y), op_(op) {}
    ~Request() {}
 
public:
    int x_;
    int y_;
    char op_;
};
 
class Response
{
public:
    // code_ result_
    std::string Serialize()
    {
        Json::Value root;
        root["code"] = code_;
        root["result"] = result_;
        Json::FastWriter writer;
        return writer.write(root);
    }
 
    bool Deserialization(std::string &str)
    {
        Json::Value root;
        Json::Reader reader;
        reader.parse(str, root);
        code_ = root["code"].asInt();
        result_ = root["result"].asInt();
        return true;
    }
 
public:
    Response(const int &result = 0, const int &code = 0) : result_(result), code_(code) {}
    ~Response() {}
 
public:
    int result_;
    int code_;
};

三、网络计算器的简单实现

        对于网络计算器的客户端,我们使用一个结构体去存储左右操作数和操作符,这个结构体我们称为请求(Request),在向服务端发送请求的时候,我们需要转成字符串才能发送,于是我们需要有序列化的函数,当然,收到结果后,我们也需要反序列化函数,将字符串结果转成结构化结果。

        对于服务端,我们使用一个结构体去存储计算结果和标识计算结果的正确性,因为客户端可能会发过来除0或者模0的请求。在收到客户端的请求后,我们需要反序列化函数将结果转成结构化数据,方便计算,发回结果的时候,需要序列化函数将结果转成字符串才能发送。

四、细节处理

        TCP是面向字节流的,所以在从网络中读取的时候,并不能保证发送和读取到的是一个完整的 x_ op_ y_ 结构,可能只发送或者读取了 x_ op_,也可能是 x_ op_ y_  x_ op_ y_  x_ op_ y_。所以客户端和服务器不能准确区分,因此我们需要对发送和读取进行控制,使得每次发送和读取到的都是一个完整的报文。

我们所使用的TCP协议是传输层协议。在TCP层,拥有两个缓冲区:发送缓冲区和接收缓冲区。我们调用的所有发送函数,并不是直接把数据发送到网络中,而是将数据由应用层拷贝到TCP的发送缓冲区中,由TCP协议决定如何发送这些数据和每次发送多少数据。接收函数也不是直接从网络中获取数据,而是从发送缓冲区拷贝数据到应用层。至于数据如何到TCP的接收缓冲区,也是完全由TCP协议决定。

因此,发送函数和接收函数本质上是拷贝函数。

因此我们可以将序列化的数据定成这种结构  length\r\nx_ op_ y_\r\n(或者 length\r\ncode_ result_\r\n) ,我们通过length来标定正文长度,使用\r\n来分隔length和正文。

知道length,就可以知道怎样读取多长的数据了,这样就可以读取到完整的报文。而发送的时候任然是面向字节流式的发送,不过我们需要添加 length 和特殊符号 \r\n,再发送。

在Protocol.hpp:协议定制里面,我们还需要修改下面的代码:Encode:将序列化的字符串转成  length\r\nx_ op_ y_\r\n。Decode:将 length\r\nx_ op_ y_\r\n 转成 x_ op_ y_(即拿到正文)。

五、守护进程

后台进程:就是在后台运行,不占用用户终端的进程。

前台进程:前台进程是与用户直接交互的进程(和终端关联的进程)。可以直接获取键盘的输入。

如下:我们的bash就是一个最常见的前台进程,我们输入各种指令,他就能返回相应的结果。

而我们上面所写的服务器进程在启动后,也是在前台运行的,也是一个前台进程。如下:

从上图我们也看到:服务器进程在启动后,如果我们再输入各种指令的话,将不会有任何结果。因为xshell登录后,只允许有一个前台进程和多个后台进程。 

再如下:我们使用管道创建多个进程,

除了PID,PPID之外,PGID我们称为组ID,SID我们称为会话ID。

这三个被同时创建的进程组成了一个进程组,他们的PGID都是23440,也是第一个进程的PID。所以一个进程组的PGID是第一个进程的PID。

我们在登陆了xshell后,xshell会给用户提供一种会话机制,在会话中,包含了给用户提供服务的bash进程,终端,以及用户自己在该会话中启动的进程。当xshell退出登录,会话也会退出。

而现在,我希望会话退出后,服务器进程任然也可以运行。那么我们就需要让服务器进程自成一个会话。这样服务器进程就成为了一个守护进程。守护进程也是一种后台进程。

我们使用 setsid()函数,就可以让某个进程变成自成会话。注:setsid要成功被调用,必须保证当前进程不是进程组的组长。

所以,我们自己写一个方法来让服务器进程变成一个守护进程。

一般步骤:

1、忽略信号:SIGPIPE,SIGCHLD

2、不要让自己是进程组的组长,fork()

3、调用setsid()函数

4、因为守护进程不能向显示器打印消息,所以我们需要将标准输出、标准错误和标准输入进行重定向。这里我们需要使用Linux中的一个文件:/dev/null。其特点就是,任何向其中写入的内容都会被其丢弃。因为文件中没有内容,所以读取时什么也读取不到。

Daemon.hpp:


#pragma once

#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

const char *root = "/";
const char *dev_null = "/dev/null";

void Daemon(bool ischdir, bool isclose)
{
    // 1. 忽略可能引起程序异常退出的信号
    signal(SIGCHLD, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);

    // 2. 让自己不要成为组长
    if (fork() > 0)
        exit(0);

    // 3. 设置让自己成为一个新的会话, 后面的代码其实是子进程在走
    setsid();

    // 4. 每一个进程都有自己的CWD,是否将当前进程的CWD更改成为 / 根目录
    if (ischdir)
        chdir(root);

    // 5. 已经变成守护进程啦,不需要和用户的输入输出,错误进行关联了
    if (isclose)
    {
        close(0);
        close(1);
        close(2);
    }
    else
    {
        // 这里一般建议就用这种
        //dev_null相当与一个垃圾回收站
        int fd = open(dev_null, O_RDWR);
        if (fd > 0)
        {
            dup2(fd, 0);
            dup2(fd, 1);
            dup2(fd, 2);
            close(fd);
        }
    }
}

        然后,我们只需要在服务器运行前调用这个函数即可。当然,服务器代码中任何向显示器打印的函数都不应该调用了。

        通过查询,发现 MyServer的父进程PPID为1,也就是操作系统。所以说,守护进程也是一种孤儿进程。

六、网络计算器完整代码

我们至此以后将sock进行封装

socket.hpp

#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define Convert(addrptr) ((struct sockaddr *)addrptr)

namespace Net_Work
{
    const static int defaultsockfd = -1;
    const int backlog = 5;

    enum
    {
        SocketError = 1,
        BindError,
        ListenError,
    };

    // 封装一个基类,Socket接口类
    // 设计模式:模版方法类
    class Socket
    {
    public:
        virtual ~Socket() {}
        virtual void CreateSocketOrDie() = 0;
        virtual void BindSocketOrDie(uint16_t port) = 0;
        virtual void ListenSocketOrDie(int backlog) = 0;
        virtual Socket *AcceptConnection(std::string *peerip, uint16_t *peerport) = 0;
        virtual bool ConnectServer(std::string &serverip, uint16_t serverport) = 0;
        virtual int GetSockFd() = 0;
        virtual void SetSockFd(int sockfd) = 0;
        virtual void CloseSocket() = 0;
        virtual bool Recv(std::string *buffer, int size) = 0;
        virtual void Send(std::string &send_str) = 0;
        // TODO
    public:
        void BuildListenSocketMethod(uint16_t port, int backlog)
        {
            CreateSocketOrDie();
            BindSocketOrDie(port);
            ListenSocketOrDie(backlog);
        }
        bool BuildConnectSocketMethod(std::string &serverip, uint16_t serverport)
        {
            CreateSocketOrDie();
            return ConnectServer(serverip, serverport);
        }
        void BuildNormalSocketMethod(int sockfd)
        {
            SetSockFd(sockfd);
        }
    };

    class TcpSocket : public Socket
    {
    public:
        TcpSocket(int sockfd = defaultsockfd) : _sockfd(sockfd)
        {
        }
        ~TcpSocket()
        {
        }
        void CreateSocketOrDie() override
        {
            _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
                exit(SocketError);
        }
        void BindSocketOrDie(uint16_t port) override
        {
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_addr.s_addr = INADDR_ANY;
            local.sin_port = htons(port);

            int n = ::bind(_sockfd, Convert(&local), sizeof(local));
            if (n < 0)
                exit(BindError);
        }
        void ListenSocketOrDie(int backlog) override
        {
            int n = ::listen(_sockfd, backlog);
            if (n < 0)
                exit(ListenError);
        }
        Socket *AcceptConnection(std::string *peerip, uint16_t *peerport) override
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int newsockfd = ::accept(_sockfd, Convert(&peer), &len);
            if (newsockfd < 0)
                return nullptr;
            *peerport = ntohs(peer.sin_port);
            *peerip = inet_ntoa(peer.sin_addr);
            Socket *s = new TcpSocket(newsockfd);
            return s;
        }
        bool ConnectServer(std::string &serverip, uint16_t serverport) override
        {
            struct sockaddr_in server;
            memset(&server, 0, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_addr.s_addr = inet_addr(serverip.c_str());
            server.sin_port = htons(serverport);

            int n = ::connect(_sockfd, Convert(&server), sizeof(server));
            if (n == 0)
                return true;
            else
                return false;
        }
        int GetSockFd() override
        {
            return _sockfd;
        }
        void SetSockFd(int sockfd) override
        {
            _sockfd = sockfd;
        }
        void CloseSocket() override
        {
            if (_sockfd > defaultsockfd)
                ::close(_sockfd);
        }
        bool Recv(std::string *buffer, int size) override
        {
            char inbuffer[size];
            ssize_t n = recv(_sockfd, inbuffer, size-1, 0);
            if(n > 0)
            {
                inbuffer[n] = 0;
                *buffer += inbuffer; // 故意拼接的
                return true;
            }
            else if(n == 0) return false;
            else return false;
        }
        void Send(std::string &send_str) override
        {
            // 多路转接我们在统一说
            send(_sockfd, send_str.c_str(), send_str.size(), 0);
        }
    private:
        int _sockfd;
    };

}

TcpServer.hpp: 


#pragma once 
#include "Socket.hpp"
#include<iostream>
#include<functional>
#include<pthread.h>

using func_t=function<string(string&,bool*error_code)>;
class TcpServer;

class ThreadData
{
public:
    ThreadData(TcpServer*tcp_this,Net_Work::Socket*sockp):_this(tcp_this),_sockp(sockp)
    {}
    TcpServer *_this;
    Net_Work::Socket*_sockp;
};

class TcpServer
{
public:
    TcpServer(uint16_t port,func_t handler_request)
        :_port(port),_listensocket(new Net_Work::TcpSocket()),_handler_request(handler_request)
        {
            _listensocket->BuildListenSocketMethod(_port,Net_Work::backlog);
        }

    static void *ThreadRun(void*args)
    {
        pthread_detach(pthread_self());
        ThreadData *td=static_cast<ThreadData*>(args);

        string inbufferstream;
        while(true)
        {
            bool ok=true;
            //读取数据----不关心数据是什么,只读取
            //1、读取报文
            if(!td->_sockp->Recv(&inbufferstream,1024))
            {
                break;
            }
            //2、处理报文
            string send_string=td->_this->_handler_request(inbufferstream,&ok);
            if(ok)
            {   
                //发送数据----不关心数据是什么,只发送
                //3、发送数据
                if(!send_string.empty())
                {
                    td->_sockp->Send(send_string);
                }
            }
            else 
            {
                break;
            }
        }
        td->_sockp->CloseSocket();
        delete td->_sockp;
        delete td;
        return nullptr;

    }

    void Loop()
    {
        while(true)
        {
            string peerip;
            uint16_t peerport;
            Net_Work::Socket*newsock=_listensocket->AcceptConnection(&peerip,&peerport);
            if(newsock==nullptr)
            {
                continue;
            }
            cout<<"获取一个新连接,socket:"<<newsock->GetSockFd()<<"client info"<<peerip<<":"<<peerport<<endl;
            pthread_t tid;
            ThreadData*td=new ThreadData(this,newsock);
            pthread_create(&tid,nullptr,ThreadRun,td);
        }
    }

    ~TcpServer()
    {}

private:
    int _port;
    Net_Work::Socket*_listensocket;
public:
    func_t _handler_request;
};

Protocol.hpp:自定义协议

我们规定序列化的结果是"x_ op_ y_”,即:左操作数,空格,操作符,空格,左操作数。

#pragma once
#include <iostream>
#include <memory>
#include <jsoncpp/json/json.h>

using namespace std;
// 问题
// 1、结构化数据的序列和反序列化
// 2、还要解决用户区分报文边界 --- 数据粘包问题
//#define SelfDefine 1

namespace Protocol
{

    const string ProtSep = " ";
    const string LineBreakSep = "\n";
    // "len\nx op y\n"  \n不属于报文的一部分,约定

    string Encode(const string &message)
    {
        string len = to_string(message.size());
        string package = len + LineBreakSep + message + LineBreakSep;
        return package;
    }

    //"len\nx op y\n""len\nx op y\n""len\nx op y\n"
    bool Decode(std::string &package, std::string *message)
    {
        // 除了解包,我还想判断报文的完整性, 能否正确处理具有"边界"的报文
        auto pos = package.find(LineBreakSep);
        if (pos == std::string::npos)
            return false;
        std::string lens = package.substr(0, pos);
        int messagelen = std::stoi(lens);
        int total = lens.size() + messagelen + 2 * LineBreakSep.size();
        if (package.size() < total)
            return false;
        // 至少package内部一定有一个完整的报文了!
        *message = package.substr(pos + LineBreakSep.size(), messagelen);
        package.erase(0, total);
        return true;
    }

    class Request
    {
    public:
        Request() : _data_x(0), _data_y(0), _oper(0)
        {
        }
        Request(int x, int y, char op) : _data_x(x), _data_y(y), _oper(op)
        {
        }

        void Debug()
        {
            cout << "_data_x" << _data_x << endl;
            cout << "_data_y" << _data_y << endl;
            cout << "_oper" << _oper << endl;
        }

        void Inc()
        {
            _data_x++;
            _data_y++;
        }
        // 序列化
        bool Serialize(string *out)
        {
#ifdef SelfDefine
            *out = to_string(_data_x) + ProtSep + _oper + ProtSep + to_string(_data_y);
            return true;
#else
            Json::Value root;
            root["datax"] = _data_x;
            root["datay"] = _data_y;
            root["oper"] = _oper;
            Json::FastWriter  writer;
            *out = writer.write(root);
            return true;
#endif
        }
        // 反序列化
        bool Deserialize(string &in)
        {
#ifdef SelfDefine
            auto left = in.find(ProtSep);
            if (left == string ::npos)
            {
                return false;
            }
            auto right = in.rfind(ProtSep);
            if (right == string::npos)
            {
                return false;
            }

            _data_x = stoi(in.substr(0, left));
            _data_y = stoi(in.substr(right + ProtSep.size()));
            string oper = in.substr(left + ProtSep.size(), right - (left + ProtSep.size()));
            if (oper.size() != 1)
            {
                return false;
            }
            _oper = oper[0];
            return true;
#else
            Json::Value root;
            Json::Reader reader;
            bool res = reader.parse(in, root);
            if (res)
            {
                _data_x = root["datax"].asInt();
                _data_y = root["datay"].asInt();
                _oper = root["oper"].asInt();
            }
            return res;
#endif
        }

        int GetX()
        {
            return _data_x;
        }
        int GetY()
        {
            return _data_y;
        }
        char GetOper()
        {
            return _oper;
        }

    private:
        int _data_x;
        int _data_y;
        char _oper;
    };

    class Response
    {
    public:
        Response() : _result(0), _code(0)
        {
        }
        Response(int result, int code) : _result(result), _code(code)
        {
        }

        bool Serialize(std::string *out)
        {
#ifdef SelfDefine

            *out = std::to_string(_result) + ProtSep + std::to_string(_code);
            return true;
#else
            Json::Value root;
            root["result"] = _result;
            root["code"] = _code;
            Json::FastWriter writer;
            *out = writer.write(root);
            return true;
#endif
        }
        bool Deserialize(std::string &in) // "_result _code" [)
        {
#ifdef SelfDefine
            auto pos = in.find(ProtSep);
            if (pos == std::string::npos)
                return false;
            _result = std::stoi(in.substr(0, pos));
            _code = std::stoi(in.substr(pos + ProtSep.size()));
            return true;
#else
            Json::Value root;
            Json::Reader reader;
            bool res = reader.parse(in, root);
            if (res)
            {
                _result = root["result"].asInt();
                _code = root["code"].asInt();
            }
            return res;
#endif
        }

        void SetResult(int res) { _result = res; }
        void SetCode(int code) { _code = code; }
        int GetResult() { return _result; }
        int GetCode() { return _code; }

    private:
        int _result;
        int _code;
    };


    //简单工厂模式,建造类设计
    class Factory
    {
    public:
        std::shared_ptr<Request> BuildRequest()
        {
            std::shared_ptr<Request> req = std::make_shared<Request>();
            return req;
        }
        std::shared_ptr<Request> BuildRequest(int x, int y, char op)
        {
            std::shared_ptr<Request> req = std::make_shared<Request>(x, y, op);
            return req;
        }
        std::shared_ptr<Response> BuildResponse()
        {
            std::shared_ptr<Response> resp = std::make_shared<Response>();
            return resp;
        }
        std::shared_ptr<Response> BuildResponse(int result, int code)
        {
            std::shared_ptr<Response> req = std::make_shared<Response>(result, code);
            return req;
        }
    };
}

CalServer.cc:


#include"Protocol.hpp"
#include"TcpServer.hpp"
#include"Calculate.hpp"
#include"Daemon.hpp"
#include<iostream>
#include<unistd.h>
#include<memory>
using namespace std;

using namespace Net_Work;
using namespace Protocol;
using namespace CalCulateNS;

// 网络负责IO发送
// HandlerRequest字节流数据解析和调用业务处理方法的
// 处理报文的方法
string HandlerRequest(string &inbufferstream,bool*error_code)
{
    *error_code=true;
    //0、计算对象
    Calculate calculate;

    //1、创建相应对象
    Factory* factory = new Factory();
    auto req=factory->BuildRequest();
    //2、分析字节流,是否有一个完整的报文
    string total_resp_string;
    string message;
    while(Decode(inbufferstream,&message))
    {
        std::cout << message << "---- messge" << std::endl;

        //3、我一定读到了一个完成的报文,可以进行反序列化了
        if(!req->Deserialize(message))
        {
            std::cout << "Deserialize error" << std::endl;
            *error_code = false;
            return string();
        }
        std::cout << "Deserialize success" << std::endl;

        //4、业务处理
        auto resp=calculate.Cal(req);

        //5、序列化response
        string send_string;
        resp->Serialize(&send_string);

        //6、构建完整的字符串级别的相应报文
        send_string=Encode(send_string);

        //7、发送
        total_resp_string+=send_string;
    }
    delete factory;
    return total_resp_string;
}

int main(int argc,char*argv[])
{
    if(argc!=2)
    {
        cout<<"Usage:"<<argv[0]<<"port"<<endl;
        return 0;
    }
    uint16_t localport=stoi(argv[1]);

    Daemon(false,false);
    unique_ptr<TcpServer> svr(new TcpServer(localport,HandlerRequest));

    svr->Loop();
    return 0;   
}

CalClient.cc:


#include "Protocol.hpp"
#include "Socket.hpp"
#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>
#include <unistd.h>

using namespace Protocol;


// ./tcpclient serverip serverport
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        std::cout << "Usage : " << argv[0] << " serverip serverport" << std::endl;
        return 0;
    }
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    Net_Work::Socket *conn = new Net_Work::TcpSocket();
    
    if (!conn->BuildConnectSocketMethod(serverip, serverport))
    {
        std::cerr << "connect " << serverip << ":" << serverport << " failed" << std::endl;
    }
    std::cout << "connect " << serverip << ":" << serverport << " success" << std::endl;

    Factory* factory = new Factory();

    srand(time(nullptr) ^ getpid());
    const std::string opers = "+-*/%^=&";
    while (true)
    {
        // 1. 构建一个请求,遵守协议
        int x = rand() % 100; //[0, 99]
        usleep(rand() % 7777);
        int y = rand() % 100; //[0,99]
        char oper = opers[rand() % opers.size()];
        std::shared_ptr<Request> req = factory->BuildRequest(x, y, oper);

        // 2. 对请求进行序列化
        std::string requeststr;
        req->Serialize(&requeststr); //

        std::cout << requeststr << std::endl;

        // for test
        std::string testreq = requeststr;
        testreq += " ";
        testreq += "= ";

        // 3. 添加自描述报头
        requeststr = Encode(requeststr);
        std::cout << requeststr << std::endl;

        // 4. 发送请求
        conn->Send(requeststr);
        std::string responsestr;
        while (true)
        {
            // 5. 读取响应
            if(!conn->Recv(&responsestr, 1024)) break;
            // 6. 报文进行解析
            std::string response;
            if (!Decode(responsestr, &response))
                continue; // 我就不连续的处理了

            // 7.response "result code"
            auto resp = factory->BuildResponse();
            resp->Deserialize(response);

            // 8. 得到了计算结果,而且是一个结构化的数据
            std::cout << testreq << resp->GetResult() << "[" << resp->GetCode() << "]" << std::endl;
            break;
        }
        sleep(1);
    }

    conn->CloseSocket();
    return 0;
}

Caculate.hpp:


#pragma once
#include <iostream>
#include <memory>
#include "Protocol.hpp"

namespace CalCulateNS
{
    using namespace std;
    enum
    {
        Sunccess = 0,
        DivZeroErr,
        ModZeroErr,
        UnknowOper
    };

    class Calculate
    {
    public:
        Calculate() {}
        shared_ptr<Protocol::Response> Cal(shared_ptr<Protocol::Request> req)
        {
            shared_ptr<Protocol::Response> resp = factory.BuildResponse();
            resp->SetCode(Sunccess);
            switch (req->GetOper())
            {
            case '+':
                resp->SetResult(req->GetX() + req->GetY());
                break;
            case '-':
                resp->SetResult(req->GetX() - req->GetY());
                break;
            case '*':
                resp->SetResult(req->GetX() * req->GetY());
                break;
            case '/':
            {
                if (req->GetY() == 0)
                {
                    resp->SetCode(DivZeroErr);
                }
                else
                {
                    resp->SetResult(req->GetX() / req->GetY());
                }
            }
            break;
            case '%':
            {
                if (req->GetY() == 0)
                {
                    resp->SetCode(ModZeroErr);
                }
                else
                {
                    resp->SetResult(req->GetX() % req->GetY());
                }
            }
            break;
            default:
                resp->SetCode(UnknowOper);
                break;
            }
            return resp;
        }

        ~Calculate() {}

    private:
        Protocol::Factory factory;
    };
}

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值