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

Linux之守护进程,应用层协议

Linux之守护进程,网络版的计算器,HTTP协议

  • 一.守护进程
    • 1.1守护进程的概念
    • 1.2守护进程的意义
    • 1.3实现守护进程
  • 二.应用层协议
    • 2.1应用层协议的含义
    • 2.1应用层协议的意义
  • 三.模拟实现网络版计算器
    • 3.1自己实现的序列化与反序列化
    • 3.1json版的序列化与反序列化

一.守护进程

在上期我们完成了Tcp和Udp服务器以及客户端的模拟实现后,我们就可以通过网络来和别人进行一些简单的交互工作,但是我们在使用的时候会发现我们必须一直开启着xshell来保持服务器的运行一旦我们将xshell关闭那么服务器立刻也会被关闭。那为什么别人家的服务器就可以一直保持运行呢?难道他们也在一直开启着“xshell”?这就要涉及到我们的守护进程了,当我们将服务器转换为守护进程后即使我们关闭了xshell但是服务器也会一直运行下去。

1.1守护进程的概念

在了解守护进程的概念之前我们要明确的一点是守护进程是一定是一个后台进程,在我们之前的学习中提到过前后台进程的关系也学习了如何将一个进程转到后台或者提到前台来。在当时的学习中我们了解了前台进程是会接收来自键盘文件的数据从而完成一些服务,所以守护进程就必须是一个后台进程因为像我们的服务器这种必须要守护进程化的进程是不需要接收键盘的数据的。也就是不需要用户进行输入而自行运行的一种进程。
所以守护进程的概念就是运行在后台的一种特殊进程,它独立于控制终端并且周期性地执行某种任务或循环等待处理某些事件的发生;它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。而Linux系统的大多数服务器就是通过守护进程实现的。

1.2守护进程的意义

在了解了守护进程的概念后守护进程的意义也就显而易见了:

  1. 守护进程是一个生存周期较长的特殊进程,通常独立于控制终端并且周期性地执行某种任务或者处理某些事件的发生。
  2. Linux的大多数服务器都是通过守护进程实现的。
  3. 关闭终端会关闭对应的进程但是守护进程就会突破这种束缚。

1.3实现守护进程

我们仔细来看守护进程的意义我们会发现第三个意义是最重要的,在我们之前的使用中一旦关闭终端后使用的进程都会一起被关闭,但是想要实现守护进程就必须要让其不被关闭。所以我们需要了解为什么关闭终端的同时进程也会被关闭。
我们先要知道我们每次退出xshell关闭的都是什么,当我们每次登入Linux后操作系统会给我们用户提供两个东西:bash内核和一个终端,有了这两个东西就可以完成对命令的解析工作所以这两个东西加起来就是一个会话。在我们退出Linux后关闭的也是这个会话以及这个会话中的进程组。所以我们想要实现守护进程我们就必须让这个进程另外创建一个会话从而实现独立出原先的那个会话的作用这样在关闭Linux就之后清除最开始的那个会话。

我已经将原理和所需要做的操作大致都说出来的但是大家可能还会很懵逼并且也会有很多的问题:会话是什么?进程组是什么?为什么关闭会话后其对应的进程组也会被关闭?如何创建一个新的会话?没关系,我来一个一个给大家解释。
我们从小到大来一步步的了解,先给大家解释一下什么是进程组和会话。
在这里插入图片描述
所以我们可以来捋一下进程,进程组和会话的关系。在一个会话中会存在着很多的进程组而这些进程组都是由一个或者多个进程形成的。所以我们创建的服务器和客户端也都是当前会话中的一个进程组。

所以我们想要创建一个守护进程就必须让他成为一个独立的会话一个不隶属于任何一个bash的会话也是这个会话中唯一的自成进程组。
所以我们现在需要了解如何创建一个新的会话:setsid()函数
在这里插入图片描述
在我们模拟实现守护进程化的代码前我们先用操作系统自己的将一个进程转换为守护进程的函数

在这里插入图片描述

在了解了系统自带的守护进程化的函数后我们就可以来模拟实现守护进程化的代码
我先把代码交给大家其中的具体含义我结合代码给大家讲解

#pragma once
#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.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.让自己不要成为进程组的组长
    pid_t id = fork();
    if (id > 0)
    {
        // 父进程
        exit(0);
    }

    // 3.设置让自己成为一个新的会话从而达成不受用户的登入和注销的影响
    setsid();

    // 4.是否将进程的cwd改为根目录
    if (ischdir)
    {
        chdir(root);
    }

    // 5.是否将进程的标准输入输出错误重定向到/dev/null
    if (isclose)
    {
        close(0);
        close(1);
        close(2);
    }
    else
    {
        int fd = open(dev_null, O_WRONLY);
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        close(fd);
    }
}

这是我们自己模拟实现的守护进程化的代码,其实只有第一步我需要和大家解释一下,剩下的步骤在上面我都提到过。为什么我们要忽略一些信号呢,这是因为在创建守护进程时可能会系统可能会接收到一些信号从而导致守护进程被异常关闭但是这个是不符合我们的预期结果的所以我们需要将这些信号进行忽略。

当一个进程守护进程化后想要关闭它现在就只剩下了一个方法那就是kill它。

二.应用层协议

2.1应用层协议的含义

在之前的Tcp服务器和客户端服务中我们完成了心跳,转换翻译服务。那么如果我们想要完成一个计算器服务的话我们有几种方式呢?很容易想到的就是我们在客户端传输形如”1+1“的字符串过去然后服务器接收字符串后分割字符串然后再进行计算但是这种方法弊端很大因为我们必须规定字符串的格式不然就会产生这样的字符串:“1 + 1”“ 1 + 1”“ 1 +1”。是否有空格空格有多少个都会让服务器的解析出现问题。
所以我们可以采用另外一种方法就是我们创建一个结构体将两个数字和符号都存储在结构体中再把数据传输给服务器来让服务器进行计算再返回答案。这个想法很好也可以避免出现上面的问题但是问题还是产生了,我们是一个网络版的计算器所以到时候肯定会有各种各样的机器来使用客户端传输数据给服务器,如果我们直接让客户端传输结构体过来机器版本的不同结构体的对齐方式,整型变量的大小都会让结构体的大小不同。那么服务器一样会解析错误所以我们就还需要想一个方法让服务器最后接收到的数据是可以通用的,这个问题其实也很简单我们只需要让客户端在发送数据之前将结构体转换为一个字符串再发送过来,服务器接收数据后再解析字符串并且将数据填入到结构体对象中,服务器发数据和客户端接收数据也是这种操作。这个方法就解决了结构体大小不同的问题而这个方法的名字就叫做序列化和反序列化大家仔细思考一下这是不是就像我们在协议中讲述的封装和解包的过程。
在这里插入图片描述

所以无论是我们使用第一种方法还是第二种方法我们只需要保证一端发送的数据可以让另外一端正确的解析就可以,这种协议这种约定就是应用层协议。

2.1应用层协议的意义

在我们了解应用层协议的含义后它的意义就很好理解了,应用层协议是计算机网络中用于实现不同应用程序之间通信的规则和标准。它们在传输层协议之上,为应用进程提供通信服务。每个应用层协议都是为了解决特定的应用问题,如域名解析、文件传输、电子邮件等。

三.模拟实现网络版计算器

3.1自己实现的序列化与反序列化

光说不练假把式我们来自己模拟实现一下有应用层协议的网络版的计算器

//Tcpserver.hpp
#pragma once
#include <iostream>
#include <string>
#include <pthread.h>
#include <functional>

#include "Socket.hpp"
#include "Protocol.hpp"

using func_t = std::function<std::string(std::string &req, bool *errno_code)>;

class TcpServer;

class ThreadDate
{
public:
    ThreadDate(TcpServer *tcp_this, MySocket::Socket *sockptr)
        : _this(tcp_this), _sockptr(sockptr)
    {
    }

    TcpServer *_this;
    MySocket::Socket *_sockptr;
};

class TcpServer
{
public:
    TcpServer(uint16_t port, func_t HandleMethod)
        : _port(port), _listensockfd(new MySocket::TcpSocket()), _HandleMethod(HandleMethod)
    {
        _listensockfd->BuildListenSocket(_port, MySocket::backlog);
    }
    ~TcpServer()
    {
        delete _listensockfd;
    }

    // 多线程执行的服务
    static void *ThreadRun(void *args)
    {
        // 线程分离
        pthread_detach(pthread_self());
        ThreadDate *td = static_cast<ThreadDate *>(args);

        // 使用循环来反复接收客户端发来的消息
        std::string inbuffer;
        while (true)
        {
            // 读取数据
            bool ok = true;
            // 1.读取报文
            if (!td->_sockptr->Recv(&inbuffer, 1024))
            {
                break;
            }
            // 2.报文处理
            std::string send_str = td->_this->_HandleMethod(inbuffer, &ok);
            if (ok)
            {
                // 3.发送数据
                if (!send_str.empty())
                {
                    td->_sockptr->Send(send_str);
                }
            }
            else
            {
                break;
            }
        }
        td->_sockptr->CloseSocket();
        delete td->_sockptr;
        delete td;
        return nullptr;
    }

    // 服务器是一个循环
    void Loop()
    {
        while (1)
        {
            // 反复进行接收连接
            std::string peer_ip;
            uint16_t peer_port;
            MySocket::Socket *newsock = _listensockfd->AcceptSocket(&peer_ip, &peer_port);
            if (newsock == nullptr)
            {
                continue;
            }
            std::cout << "获得一个新连接,sockfd:" << newsock->GetSocketfd() << " client info:" << peer_ip << "-" << peer_port << std::endl;

            // 利用多线程来让执行服务
            pthread_t tid;
            ThreadDate *td = new ThreadDate(this, newsock);
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }

private:
    uint16_t _port;//端口号
    MySocket::Socket *_listensockfd;//监听的文件描述符

public:
    func_t _HandleMethod; //回调函数
};
//Tcpserver.cc
#include <iostream>
#include <memory>

#include "Protocol.hpp"
#include "Tcpserver.hpp"
#include "Calculator.hpp"

using namespace Protocol;

std::string HandleMethod(std::string inbuffer, bool *error_code)
{
    *error_code = true;
    // 1.创建计算机对象
    Calculator::Calculator cal;

    // 2.创建响应对象
    std::unique_ptr<Protocol::Factory> factory = std::make_unique<Protocol::Factory>();
    auto req = factory->BuildRequest();

    // 3.分析字节流,判断是否有一个完整的报文
    std::string resp_string;
    std::string message;
    while (Protocol::Decode(inbuffer, &message))
    {
        // std::cout << "message: " << message << std::endl;
        //  4.读取到了一个完整的报文后进行反序列化
        if (!req->Deserializa(message))
        {
            std::cout << "Deserializa error" << std::endl;
            *error_code = false;
            return std::string();
        }
        std::cout << "Deserializa success" << std::endl;

        // 5.进行计算
        auto resp = cal.Cal(req);

        // 6.序列化response
        std::string send_str;
        resp->Serialization(&send_str);

        // 7.将response增加自定义报头
        send_str = Protocol::Encode(send_str);

        // 8.发送
        resp_string = send_str;
        std::cout << resp_string << std::endl;
    }
    return resp_string;
}

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

    std::unique_ptr<TcpServer> svr(new TcpServer(localport, HandleMethod));

    svr->Loop();
    return 0;
}
//Tcpclient.hpp
#include <iostream>
#include <string>
#include "Socket.hpp"
#include "Protocol.hpp"

using namespace Protocol;

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        std::cout << "Usge:\n"
                  << argv[0] << " serverip serverport" << std::endl;
    }

    std::string server_ip = argv[1];
    uint16_t server_port = std::stoi(argv[2]);

    MySocket::Socket *connectfd = new MySocket::TcpSocket();

    if (!connectfd->BuildConnectSocket(server_ip, server_port))
    {
        std::cerr << "connect " << server_ip << "-" << server_port << " fail" << std::endl;
    }
    std::cout << "connect " << server_ip << "-" << server_port << " success" << std::endl;

    std::unique_ptr<Factory> factory = std::make_unique<Factory>();

    srand(getpid() ^ time(nullptr));
    const std::string opers = "+-*/%^=#";

    while (true)
    {
        // 1.构建一个请求并且遵守我们的协议
        int x = rand() % 100;
        usleep(rand() % 7897);
        int y = rand() % 100;
        char oper = opers[rand() % opers.size()];
        std::shared_ptr<Request> req = factory->BuildRequest(x, y, oper);

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

        // 3.添加自定义的报头
        requeststr = Protocol::Encode(requeststr);

        // 4.发送请求
        connectfd->Send(requeststr);
        std::string responsestr;
        std::cout << requeststr << std::endl;

        // 发送请求后就要反复接收响应并且处理返回完整的报文
        while (true)
        {
            // 5.接收响应
            if (!connectfd->Recv(&responsestr, 1024))
            {
                break;
            }

            // 6.对报文进行解析
            std::string response;
            if (!Protocol::Decode(responsestr, &response))
            {
                continue;
            }
            // 7.将报文反序列化
            auto resp = factory->BuildResponse();
            resp->Deserializa(response);

            // 8.打印出结果
            std::cout << "result:" << resp->GetResult() << "[" << resp->GetCode() << "]" << std::endl;
            break;
        }
        sleep(1);
    }
    connectfd->CloseSocket();
    return 0;
}
//Socket.hpp
#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

#define CONV(addrprt) ((struct sockaddr *)addrprt)

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

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

    // 自定义套接字基类
    // 设计模式:模板方法类
    //这个设计模式是我们利用多态来实现的
    class Socket
    {
    public:
        //使用virtual关键字让基类的函数变成虚函数
        virtual ~Socket() {};
        virtual void CreateSocket() = 0;
        virtual void BindSocket(uint16_t port) = 0;
        virtual void ListenSocket(int backlog) = 0;
        virtual Socket *AcceptSocket(std::string *peer_ip, uint16_t *peer_port) = 0;
        virtual bool ConnectSocket(std::string &server_ip, uint16_t server_port) = 0;
        virtual int GetSocketfd() = 0;
        virtual void SetSocketfd(int sockfd) = 0;
        virtual void CloseSocket() = 0;
        virtual bool Recv(std::string *recv_str, int size) = 0;
        virtual void Send(std::string send_str) = 0;

    public:
        // 创建Tcpserver的套接字
        void BuildListenSocket(uint16_t port, int backlog)
        {
            CreateSocket();
            BindSocket(port);
            ListenSocket(backlog);
        }

        // 创建Tcpclient的套接字
        bool BuildConnectSocket(std::string &server_ip, uint16_t server_port)
        {
            CreateSocket();
            return ConnectSocket(server_ip, server_port);
        }

        void BuildNormalSocket(int sockfd)
        {
            SetSocketfd(sockfd);
        }
    };

    class TcpSocket : public Socket
    {
    public:
        TcpSocket(int sockfd = defaultsockfd)
            : _sockfd(sockfd)
        {
        }
        ~TcpSocket()
        {
        }

        //子类则使用override关键字来覆盖
        void CreateSocket() override
        {
            _sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                exit(SocketError);
            }
        }
        void BindSocket(uint16_t port) override
        {
            struct sockaddr_in local;
            local.sin_family = AF_INET;
            local.sin_addr.s_addr = INADDR_ANY;
            local.sin_port = htons(port); // 将端口号网络序列化

            int n = bind(_sockfd, CONV(&local), sizeof(local));
            if (n < 0)
            {
                exit(BindError);
            }
        }
        void ListenSocket(int backlog) override
        {
            int n = listen(_sockfd, backlog);
            if (n < 0)
            {
                exit(ListenError);
            }
        }
        Socket *AcceptSocket(std::string *peer_ip, uint16_t *peer_port) override
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int newsockfd = accept(_sockfd, CONV(&peer), &len);
            if (newsockfd < 0)
            {
                return nullptr;
            }
            *peer_ip = inet_ntoa(peer.sin_addr); // 将IP地址转为主机序列
            *peer_port = ntohs(peer.sin_port);
            Socket *s = new TcpSocket(newsockfd);
            return s;
        }
        bool ConnectSocket(std::string &server_ip, uint16_t server_port) override
        {
            struct sockaddr_in server;
            memset(&server, 0, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_port = htons(server_port);
            server.sin_addr.s_addr = inet_addr(server_ip.c_str());

            int n = connect(_sockfd, CONV(&server), sizeof(server));
            if (n < 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        int GetSocketfd() override
        {
            return _sockfd;
        }

        void SetSocketfd(int sockfd) override
        {
            _sockfd = sockfd;
        }

        void CloseSocket() override
        {
            if (_sockfd > defaultsockfd)
            {
                close(_sockfd);
            }
        }

        //接收响应
        bool Recv(std::string *recv_str, int size) override
        {
            char inbuffer[size];
            ssize_t n = recv(_sockfd, &inbuffer, size - 1, 0);
            if (n > 0)
            {
                inbuffer[n] = 0;
                *recv_str += 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;
    };
}

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

namespace Protocol
{

    const std::string Prosep = " ";
    const std::string LineBreaksep = "\n";

    // 增加自定义报头len=报文的大小
    // 自定义报头的意义就是更加方便的判断报文是否完整
    //"len\nx oper y\n"
    std::string Encode(const std::string &message)
    {
        std::string len = std::to_string(message.size());
        std::string package = len + LineBreaksep + message + LineBreaksep;
        return package;
    }

    // 解除自定义报头并且判定是否是一个完整的报文
    // 报文会有很多种情况
    //"len"
    //"len\n"
    //"len\nx"
    //"len\nx "
    //"len\nx oper"
    //"len\nx oper "
    //"len\nx oper y"
    //"len\nx oper y\n"
    //"len\nx oper y\nlen"
    //"len\nx oper y\nlen\n"
    //"len\nx oper y\nlen\nx"
    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 messagesize = std::stoi(lens);
        int total = lens.size() + messagesize + 2 * LineBreaksep.size();
        if (package.size() < total)
        {
            return false;
        }

        *message = package.substr(pos + LineBreaksep.size(), messagesize);
        package.erase(0, total);
        return true;
    }

    // 请求类
    class Request
    {
    public:
        Request()
            : _date_x(0), _date_y(0), _oper(0)
        {
        }
        Request(int x, int y, char oper)
            : _date_x(x), _date_y(y), _oper(oper)
        {
        }
        ~Request()
        {
        }
        void Debug()
        {
            std::cout << _date_x << std::endl;
            std::cout << _date_y << std::endl;
            std::cout << _oper << std::endl;
        }
        // 将结构化数据转为字符串也就是序列化
        // 序列化结果为"x oper y"
        bool Serialization(std::string *out)
        {
            *out = std::to_string(_date_x) + Prosep + _oper + Prosep + std::to_string(_date_y);
            return true;
        }

        // 将字符串转为结构体数据也就是反序列化
        //"x oper y"
        bool Deserializa(std::string &in)
        {
            auto left = in.find(Prosep);
            if (left == std::string::npos)
            {
                return false;
            }
            auto right = in.rfind(Prosep);
            if (right == std::string::npos)
            {
                return false;
            }
            _date_x = std::stoi(in.substr(0, left));
            _date_y = std::stoi(in.substr(right + Prosep.size()));
            std::string oper = in.substr(left + Prosep.size(), right - (left + Prosep.size()));

            if (oper.size() != 1)
            {
                return false;
            }
            _oper = oper[0];
            return true;
        }

        int Getx()
        {
            return _date_x;
        }

        int Gety()
        {
            return _date_y;
        }

        char Getoper()
        {
            return _oper;
        }

    private:
        int _date_x; // 第一个参数
        int _date_y; // 第二个参数
        char _oper;  // 符号
    };

    // 响应类
    class Response
    {
    public:
        Response()
            : _result(0), _code(0)
        {
        }
        Response(int result, int code)
            : _result(result), _code(code)
        {
        }
        ~Response()
        {
        }

        // 将结构化数据序列化
        // 序列化结果:"result code"
        bool Serialization(std::string *out)
        {
            *out = std::to_string(_result) + Prosep + std::to_string(_code);
            return true;
        }

        // 反序列化
        //"result code"
        bool Deserializa(std::string &in)
        {
            auto left = in.find(Prosep);
            if (left == std::string::npos)
            {
                return false;
            }

            _result = std::stoi(in.substr(0, left));
            _code = std::stoi(in.substr(left + Prosep.size()));
            return true;
        }

        void SetResult(int result)
        {
            _result = result;
        }

        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 oper)
        {
            std::shared_ptr<Request> req = std::make_shared<Request>(x, y, oper);
            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> resp = std::make_shared<Response>(result, code);
            return resp;
        }
    };
}

//Calculator.hpp
#pragma once
#include <iostream>
#include <memory>

#include "Protocol.hpp"

namespace Calculator
{
    enum
    {
        Success = 0,
        DivZeroErr,
        ModZeroErr,
        UnKnowErr,
    };

    class Calculator
    {

    public:
        Calculator()
        {
        }
        std::shared_ptr<Protocol::Response> Cal(std::shared_ptr<Protocol::Request> req)
        {
            std::shared_ptr<Protocol::Response> resp = factory.BuildResponse();
            resp->SetCode(Success);
            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(UnKnowErr);
                break;
            }
            return resp;
        }

    private:
        Protocol::Factory factory;
    };

}

#Makefile
.PHONY:all
all:tcp_client tcp_server

tcp_client:Tcpclient.cc
	g++ -o $@ $^ -std=c++14 
tcp_server:Tcpserver.cc
	g++ -o $@ $^ -std=c++14 -lpthread

.PHONY:clean
clean:
	rm -f tcp_client tcp_server

这就是完整的代码了,但是这其中仍然有一个问题可能大家没有很理解:为什么可能会接收到不是完整的报文?
这就和Tcp协议有关了因为Tcp协议是面向字节流的,所以无论是send/write还是recv/read都是一个拷贝函数,这些函数的作用就是将序列化或者反序列化后的数据拷贝到服务器和客户端中的缓冲区中但是这些数据什么时候发?发多少?出错了怎么办都和他们没有关系这些问题都是内核决定的也就是内核中的Tcp协议决定的。所以会出现客户端发送了很多次数据后服务器才接收一次数据从而导致接收到很多个报文,而且又由于发多少也是不固定的也就会产生不完整的报文的情况。所以我们用户使用send/write函数时我们以为我们发送了1024个字节但是这只是我们想得而已不代表服务器真的会接收1024个字节,这些都是由Tcp协议来决定的是我们无法控制的。而面向字节流的意思也就是面向这一个一个的字节,发送和接收都是以字节为单位的。
但是像Udp这种面向数据包的协议,在发送的时候就会将一整个数据包的内容全部发送过去数据包就类似我们的一整个字符串。
在这里插入图片描述

3.1json版的序列化与反序列化

上面的代码是我们自己模拟实现的序列化与反序列化但是其实是有专门进行序列化和反序列化的一个工具叫做json。
JSON,全称为JavaScript Object Notation,即JavaScript对象表示法,是一种轻量级的数据交换格式。它基于JavaScript语言的一个子集,但JSON是完全独立于语言的,这意味着它可以被许多不同的编程语言所解析和生成。
在使用json前我们需要在Linux中下载json库这个大家可以去网上找一搜就有我就不赘述了。
因为我们只是用json来完成序列化和反序列化的工作所以我们只需要了解一点点的json知识就可以了,如果大家想要深入学习json可以自行学习。

// JsonProtocol.hpp
#pragma once
#include <iostream>
#include <memory>
// 使用json必须包含的一个头文件
#include <jsoncpp/json/json.h>
namespace Protocol
{

    const std::string Prosep = " ";
    const std::string LineBreaksep = "\n";

    // 增加自定义报头
    std::string Encode(const std::string &message)
    {
        std::string len = std::to_string(message.size());
        std::string package = len + LineBreaksep + message + LineBreaksep;
        return package;
    }

    // 解除自定义报头并且判定是否是一个完整的报文
    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 messagesize = std::stoi(lens);
        int total = lens.size() + messagesize + 2 * LineBreaksep.size();
        if (package.size() < total)
        {
            return false;
        }

        *message = package.substr(pos + LineBreaksep.size(), messagesize);
        package.erase(0, total);
        return true;
    }

    class Request
    {
    public:
        Request()
            : _date_x(0), _date_y(0), _oper(0)
        {
        }
        Request(int x, int y, char oper)
            : _date_x(x), _date_y(y), _oper(oper)
        {
        }
        ~Request()
        {
        }
        void Debug()
        {
            std::cout << _date_x << std::endl;
            std::cout << _date_y << std::endl;
            std::cout << _oper << std::endl;
        }
        // 将结构化数据转为字符串也就是序列化
        bool Serialization(std::string *out)
        {
            // 在使用json时要先创建出一个json对象
            Json::Value root;
            // json的使用像是我们之前学习的map它是一种kv结构
            // 所以我们把数据当作v值即可
            root["x"] = _date_x;
            root["y"] = _date_y;
            root["oper"] = _oper;

            // 进行序列化
            // 在json中有专门用于序列化的函数
            Json::FastWriter write;
            *out = write.write(root);

            return true;
        }

        // 将字符串转为结构体数据也就是反序列化
        bool Deserializa(std::string &in)
        {
            Json::Value root;
            Json::Reader read;
            // json也有专门反序列化的函数
            bool res = read.parse(in, root);
            if (res)
            {
                // 因为返回的时候是返回一个字符串,
                // 所以我们可以利用json对象自带的将返回值转换为整型的函数
                // ps:char类型也是整型类型,只是会转为对应的ASCII值
                _date_x = root["x"].asInt();
                _date_y = root["y"].asInt();
                _oper = root["oper"].asInt();
            }
            return res;
        }

        int Getx()
        {
            return _date_x;
        }

        int Gety()
        {
            return _date_y;
        }

        char Getoper()
        {
            return _oper;
        }

    private:
        int _date_x; // 第一个参数
        int _date_y; // 第二个参数
        char _oper;  // 符号
    };

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

        // 将结构化数据序列化
        bool Serialization(std::string *out)
        {
            Json::Value root;
            root["result"] = _result;
            root["code"] = _code;

            Json::FastWriter write;
            *out = write.write(root);

            return true;
        }

        // 反序列化
        bool Deserializa(std::string &in)
        {
            Json::Value root;
            Json::Reader read;
            bool res = read.parse(in, root);
            if (res)
            {
                _result = root["result"].asInt();
                _code = root["code"].asInt();
            }
            return res;
        }

        void SetResult(int result)
        {
            _result = result;
        }

        void SetCode(int code)
        {
            _code = code;
        }

        int GetResult()
        {
            return _result;
        }

        int GetCode()
        {
            return _code;
        }

        void Debug()
        {
            std::cout << _result << std::endl;
            std::cout << _code << std::endl;
        }

    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 oper)
        {
            std::shared_ptr<Request> req = std::make_shared<Request>(x, y, oper);
            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> resp = std::make_shared<Response>(result, code);
            return resp;
        }
    };
}


http://www.kler.cn/a/535346.html

相关文章:

  • android 打包AAR-引入资源layout-安卓封包
  • 算法设计与分析三级项目--管道铺设系统
  • systemverilog的program和module的区别
  • 【学Rust写CAD】3 绝对坐标系详解
  • Rapidjson 实战
  • 解锁.NET Fiddle:在线编程的神奇之旅
  • 【数据结构】_队列的结构与实现
  • 【Leetcode 每日一题】90. 子集 II
  • 基于多重算法的医院增强型50G全光网络设计与实践:构建智慧医疗新基石(上)
  • MS SQL Server partition by 函数实战二 编排考场人员
  • vite共享配置之---css相关
  • 【玩转 Postman 接口测试与开发2_018】第14章:利用 Postman 初探 API 安全测试
  • MAC OS安装Homebrew
  • android用eclipse开发碰到65535问题的完美解决方案
  • DMZ区的作用和原则
  • 【Windows 开发NVIDIA相关组件】CUDA、cuDNN、TensorRT
  • 3.5 Go(特殊函数)
  • 计算机毕业设计hadoop+spark+hive民宿推荐系统 酒店推荐系统 民宿价格预测 酒店价预测 机器学习 深度学习 Python爬虫 HDFS集群
  • 嵌入式硬件篇---OpenMV基本使用自动增益\曝光\白平衡
  • Unity VideoPlayer播放视屏不清晰的一种情况
  • 网络安全风险量化值 网络安全风险控制
  • C# OpenCV机器视觉:利用TrashNet实现垃圾分类
  • Google地图瓦片爬虫——进阶版
  • 计算机网络之物理层通信基础(电路交换、报文交换与分组交换)
  • go函数详解
  • ONLYOFFICE 文档 8.3 已发布:PDF 图章、合并形状、更多格式支持等