序列化反序列化(自己实现版)+协议定制+守护进程

Makefile

.PHONY:all
all:client server

client:CalClient.cc
	g++ -o client CalClient.cc -std=c++11 -lpthread
server:CalServer.cc
	g++ -o server CalServer.cc -std=c++11 -lpthread

.PHONY:clean
clean:
	rm -f client server

Log.hpp

#pragma once
#include <iostream>
#include <string>
#include <cstdarg>
#include <cstdio>
#include <ctime>
 
#define DEBUG 0
#define NORMAL 1
#define WARNING 2
#define ERROR 3
#define FATAL 4
 
const char *gLevelMap[5] = {"DEBUG", "NORMAL", "WARNING", "ERROR", "FATAL"};
 
#define LOGFILE "./threadpool.log"
 
void logMessage(int level, const char *format, ...)
{
    char stdBuffer[1024]; // 标准部分
    time_t timestamp = time(nullptr);
    struct tm *time = localtime(&timestamp);
    snprintf(stdBuffer, sizeof stdBuffer, "[%s][%ld]", gLevelMap[level], timestamp);
 
    char logBuffer[1024]; // 自定义部分
    va_list args;
    va_start(args, format);
    vsnprintf(logBuffer, sizeof(logBuffer), format, args); // 使用 vsnprintf 而不是 vsprintf
    va_end(args);
 
    FILE* fp=fopen(LOGFILE,"a");
 
    // 打印到显示器
    // printf("%s %s\n", stdBuffer, logBuffer);
 
    // 打印到指定文件
    fprintf(fp,"%s%s\n",stdBuffer,logBuffer);
 
    fclose(fp);
}

Protocol.hpp

#pragma once

#include <iostream>
#include <string>
#include <cstring>

namespace ns_protocal
{
#define MYSELF 1
#define SPACE " "
#define SPACE_LEN strlen(SPACE)

#define  SEP "\r\n"
#define SEP_LEN strlen(SEP)

    class Request
    {
    public:
        std::string Serialize()
        {
#ifdef MYSELF
            std::string str;
            str = std::to_string(_x);
            str += SPACE;
            str += _op;
            str += SPACE;
            str += std::to_string(_y);
            return str;
#else
            std::cout << "TODO" << std::endl;
#endif
        }

        bool Deserialized(const std::string &str)
        {
#ifdef MYSELF
            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 + SPACE_LEN).c_str());
            if (left + SPACE_LEN > str.size())
                return false;
            else
                _op = str[left + SPACE_LEN];

#else
            std::cout << "TODO" << std::endl;
#endif
        }

    public:
        Request(int x, int y, char op)
            : _x(x), _y(y), _op(op)
        {
        }

        Request() {}

        Request(const Request &copy)
        {
            _x = copy._x;
            _y = copy._y;
            _op = copy._op;
        }

        ~Request() {}

    public:
        int _x;
        int _y;
        char _op;
    };

    class Response
    {
    public:
        //"_code _result"
        std::string Serialize()
        {

#ifdef MYSELF
            std::string s;
            s = std::to_string(_code);
            s += SPACE;
            s += std::to_string(_result);

            return s;
#endif
        }

        //"1 0"
        bool Deserialized(const std::string &s)
        {
#ifdef MYSELF
            std::size_t pos = s.find(SPACE);
            if (pos == std::string::npos)
                return false;
            _code = atoi(s.substr(0, pos).c_str());
            _result = atoi(s.substr(pos + SPACE_LEN).c_str());
            return true;
#endif
        }

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

        Response() {}

        ~Response() {}

    public:
        int _result; // 计算结果
        int _code;   // 计算结果的状态码
    };

    bool Recv(int sock, std::string *out)
    {
        char buffer[1024];
        ssize_t s = recv(sock, buffer, sizeof(buffer), 0);
        if (s > 0)
        {
            buffer[s] = 0;
            *out += buffer;
        }
        else if (s == 0)
        {
            std::cout << "client quit" << std::endl;
            return false;
        }
        else
        {
            std::cout << "recv error" << std::endl;
            return false;
        }
        return true;
    }

    void Send(int sock, const std::string str)
    {
        std::cout << "send in" << std::endl;
        int n = send(sock, str.c_str(), str.size(), 0);
        if (n < 0) // 客户端关闭
            std::cout << "send error" << std::endl;
    }

    std::string Decode(std::string &buffer)
    {
        std::size_t pos = buffer.find(SEP);
        if (pos == std::string::npos)
            return "";
        int size = atoi(buffer.substr(0, pos).c_str());
        int surplus = buffer.size() - pos - 2 * SEP_LEN;
        if (surplus >= size)
        {
            buffer.erase(0, pos + SEP_LEN);
            std::string s = buffer.substr(0, size);
            buffer.erase(0, size + SEP_LEN);
            return s;
        }
        else
        {
            return "";
        }
    }

    std::string Encode(std::string &s)
    {
        std::string new_package = std::to_string(s.size());
        new_package += SEP;
        new_package += s;
        new_package += SEP;

        return new_package;
    }
}

Sock.hpp

#pragma once

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

#include "Log.hpp"
#include <memory>

class Sock
{
private:
    const static int gbacklog = 20;

public:
    Sock() {}
    int Socket()
    {
        int listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock < 0)
        {
            logMessage(FATAL, "create socket error,%d:%s", errno, strerror(errno));
            exit(2);
        }
        logMessage(NORMAL, "create socket success, listensock: %d", listensock);

        return listensock;
    }

    void Bind(int sock, uint16_t port, std::string ip = "0.0.0.0")
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        inet_pton(AF_INET, ip.c_str(), &local.sin_addr);
        // local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
        if (bind(sock, (const sockaddr *)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "bind error, %d:%s", errno, strerror(errno));
            exit(3);
        }
    }

    void Listen(int sock)
    {

        if (listen(sock, gbacklog) < 0)
        {
            logMessage(FATAL, "listen error, %d:%s", errno, strerror(errno));
            exit(4);
        }
        logMessage(NORMAL, "listen server success");
    }

    int Accept(int listensock, std::string *ip, uint16_t *port)
    {
        struct sockaddr_in src;
        memset(&src, 0, sizeof(src));
        socklen_t len = sizeof(src);
        int servicesock = accept(listensock, (struct sockaddr *)&src, &len);
        if (servicesock < 0)
        {
            logMessage(ERROR, "accept error,%d:%s", errno, strerror(errno));
            return -1;
        }
        if (port)
            *port = ntohs(src.sin_port);
        if (ip)
            *ip = inet_ntoa(src.sin_addr);
        return servicesock;
    }

    bool Connect(int sock, const std::string &server_ip, const uint16_t &server_port)
    {
        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());

        if (connect(sock, (struct sockaddr *)&server, sizeof(server)) == 0)
            return true;
        else
            return false;
    }

    ~Sock()
    {
    }
};

TcpServer.hpp

#pragma once

#include "Sock.hpp"
#include <functional>
#include <pthread.h>
#include <vector>
#include <unordered_map>
#include <unistd.h>

namespace ns_tcpserver
{

    using func_t = std::function<void(int)>;

    class TcpServer;
    class ThreadData
    {
    public:
        ThreadData(int sock, TcpServer *server)
            : _sock(sock), _server(server)
        {
        }

    public:
        int _sock;
        TcpServer *_server;
    };

    class TcpServer
    {
    private:
        static void *ThreadRoutine(void *args)
        {
            pthread_detach(pthread_self());
            ThreadData *td = static_cast<ThreadData *>(args);
            td->_server->Excute(td->_sock);

            close(td->_sock);
            delete td;

            return nullptr;
        }

    public:
        TcpServer(const uint16_t &port, const std::string &ip = "0.0.0.0")
        {
            _listensock = _sock.Socket();
            _sock.Bind(_listensock, port, ip);
            _sock.Listen(_listensock);
        }

        void BindService(func_t func)
        {
            _funcs.push_back(func);
        }

        void Excute(int sock)
        {
            for (auto &f : _funcs)
            {
                f(sock);
            }
        }

        void Start()
        {
            while (true)
            {
                std::string clientip;
                uint16_t clientport;
                int servicesock = _sock.Accept(_listensock, &clientip, &clientport);
                if (servicesock == -1)
                    continue;
                logMessage(NORMAL, "create new link success, sock: %d", servicesock);

                pthread_t tid;
                ThreadData *td = new ThreadData(servicesock, this);
                pthread_create(&tid, nullptr, ThreadRoutine, td);
            }
        }

        ~TcpServer()
        {
            if (_listensock >= 0)
            {
                close(_listensock);
            }
        }

    private:
        int _listensock;
        Sock _sock;

        std::vector<func_t> _funcs;
        // std::unordered_map<std::string, func_t> _func;
    };
}

CalServer.cc

#include "TcpServer.hpp"
#include "Protocol.hpp"
#include <memory>
#include "Daemon.hpp"

using namespace ns_tcpserver;
using namespace ns_protocal;

void Usage(const char *args)
{
    std::cout << "\nUsage: " << args << " port" << std::endl;
}

Response calculatorHelp(const Request &req);

void calculator(int sock)
{
    std::string inbuffer;
    while (true)
    {
        // 1.读取数据
        bool res = Recv(sock, &inbuffer);
        if (!res)
            break;
        // 读取成功
        // 2.协议解析,保证得到一个完整的报文
        std::string package = Decode(inbuffer);
        if (package.empty())
            continue;
        // 3.保证该报文是一个完整的报文
        logMessage(NORMAL, "%s", package.c_str());
        Request req;
        // 4.反序列化 字节流->结构化
        req.Deserialized(package);
        // 5.业务逻辑
        Response resp = calculatorHelp(req);
        // 序列化
        std::string respString = resp.Serialize();
        // 7.添加长度信息,形成一个完整的报文
        //"length\r\ncode result\r\n
        respString = Encode(respString);

        Send(sock, respString);
    }
}

Response calculatorHelp(const Request &req)
{
    Response resp(0, 0);
    switch (req._op)
    {
    case '+':
        resp._result = req._x + req._y;
        break;
    case '-':
        resp._result = req._x - req._y;
        break;
    case '*':
        resp._result = req._x * req._y;
        break;
    case '/':
        if (req._y == 0)
        {
            resp._code = 1;
        }
        else
        {
            resp._result = req._x / req._y;
        }
        break;
    case '%':
        if (req._y == 0)
            resp._code = 2;
        else
            resp._result = req._x % req._y;
        break;
    default:
        resp._code = 3;
        break;
    }
    return resp;
}

void handler(int signo)
{
    std::cout << "get a signo: " << signo << std::endl;
    exit(0);
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(1);
    }
    //signal(SIGPIPE, SIG_IGN);

    std::unique_ptr<TcpServer> server(new TcpServer(atoi(argv[1])));
    server->BindService(calculator);
    MyDaemon();
    server->Start();

    // Request req(123, 456, '+');
    // std::string s = req.Serialize();
    // std::cout << s << std::endl;

    // Request temp;
    // temp.Deserialized(s);
    // std::cout << temp._x << std::endl;
    // std::cout << temp._op << std::endl;
    // std::cout << temp._y << std::endl;

    return 0;
}

CalClient.cc

#include <iostream>
#include "Sock.hpp"
#include "Protocol.hpp"
#include <unistd.h>

using namespace ns_protocal;

void Usage(const char *args)
{
    std::cout << "\nUsage: " << args << " serverIp serverPort" << std::endl;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    std::string server_ip = argv[1];
    uint16_t server_port = atoi(argv[2]);
    Sock sock;
    int sockfd = sock.Socket();
    if (!sock.Connect(sockfd, server_ip, server_port))
    {
        std::cerr << "Connect error" << std::endl;
        exit(2);
    }

    bool quit = false;
    while (!quit)
    {
        // 1.获取需求
        Request req(10, 20, '+');
        std::cout << "Please Enter # ";
        std::cin >> req._x >> req._op >> req._y;

        // 2.序列化
        std::string s = req.Serialize();
        std::string temp = s;
        // 3.添加长度报头
        s = Encode(s);
        // 4.发送给服务端
        Send(sockfd, s);

        // 5.正常读取
        while (true)
        {
            std::string buffer;
            bool res = Recv(sockfd, &buffer);
            if (!res)
            {
                quit = true;
                break;
            }
            std::string package = Decode(buffer);
            if (package.empty())
                continue;

            Response resp;
            resp.Deserialized(package);
            std::string err;
            switch (resp._code)
            {
            case 1:
                err = "除0错误";
                break;
            case 2:
                err = "模0错误";
                break;
            case 3:
                err = "非法操作";
                break;
            default:
                std::cout << temp << " =" << resp._result << "[success]" << std::endl;
                break;
            }
            if (!err.empty())
                std::cerr << err << std::endl;
            break;
        }
    }
    close(sockfd);
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值