【Linux】网络版计算器代码

网络应用层协议的demo  运输层基于TCP

protocol.hpp

#pragma once

#include 
#include 
#include 
#include 
#include 
#include 

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

enum
{
    OK = 0,
    DIV_ZERO,
    MOD_ZERO,
    OP_ERROR
};

std::string enLength(const std::string &text)
{
    std::string send_string = std::to_string(text.size());
    send_string += LINE_SEP;
    send_string += text;
    send_string += LINE_SEP;

    return send_string;
}

bool deLength(const std::string &package, std::string *text)
{
    auto pos = package.find(LINE_SEP);
    if (pos == std::string::npos)
        return false;
    std::string text_len_string = package.substr(0, pos);
    int text_len = std::stoi(text_len_string);
    *text = package.substr(pos + LINE_SEP_LEN, text_len);
    return true;
}


class Request
{
public:
    Request() : x(0), y(0), op(0)
    {
    }
    Request(int x_, int y_, char op_) : x(x_), y(y_), op(op_)
    {
    }
    bool serialize(std::string *out)
    {
#ifdef MYSELF
        *out = "";
        // 结构化 -> "x op y";
        std::string x_string = std::to_string(x);
        std::string y_string = std::to_string(y);

        *out = x_string;
        *out += SEP;
        *out += op;
        *out += SEP;
        *out += y_string;
#else
        Json::Value root;
        root["first"] = x;
        root["second"] = y;
        root["oper"] = op;

        Json::FastWriter writer;
        // Json::StyledWriter writer;
        *out = writer.write(root);
#endif
        return true;
    }

    bool deserialize(const std::string &in)
    {
#ifdef MYSELF
        // "x op y" -> 结构化
        auto left = in.find(SEP);
        auto right = in.rfind(SEP);
        if (left == std::string::npos || right == std::string::npos)
            return false;
        if (left == right)
            return false;
        if (right - (left + SEP_LEN) != 1)
            return false;

        std::string x_string = in.substr(0, left);
        std::string y_string = in.substr(right + SEP_LEN);

        if (x_string.empty())
            return false;
        if (y_string.empty())
            return false;
        x = std::stoi(x_string);
        y = std::stoi(y_string);
        op = in[left + SEP_LEN];
#else
        Json::Value root;
        Json::Reader reader;
        reader.parse(in, root);

        x = root["first"].asInt();
        y = root["second"].asInt();
        op = root["oper"].asInt();
#endif
        return true;
    }

public:
    int x;
    int y;
    char op;
};

class Response
{
public:
    Response() : exitcode(0), result(0)
    {
    }
    Response(int exitcode_, int result_) : exitcode(exitcode_), result(result_)
    {
    }
    bool serialize(std::string *out)
    {
#ifdef MYSELF
        *out = "";
        std::string ec_string = std::to_string(exitcode);
        std::string res_string = std::to_string(result);

        *out = ec_string;
        *out += SEP;
        *out += res_string;
#else
        Json::Value root;
        root["exitcode"] = exitcode;
        root["result"] = result;

        Json::FastWriter writer;
        *out = writer.write(root);
#endif
        return true;
    }
    bool deserialize(const std::string &in)
    {
#ifdef MYSELF
        // "exitcode result"
        auto mid = in.find(SEP);
        if (mid == std::string::npos)
            return false;
        std::string ec_string = in.substr(0, mid);
        std::string res_string = in.substr(mid + SEP_LEN);
        if (ec_string.empty() || res_string.empty())
            return false;

        exitcode = std::stoi(ec_string);
        result = std::stoi(res_string);
#else
        Json::Value root;
        Json::Reader reader;
        reader.parse(in, root);
        
        exitcode = root["exitcode"].asInt();
        result = root["result"].asInt();
#endif
        return true;
    }

public:
    int exitcode; // 0:计算成功,!0表示计算失败
    int result;   // 计算结果
};

bool recvPackage(int sock, std::string &inbuffer, std::string *text)
{
    char buffer[1024];
    while (true)
    {
        ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            inbuffer += buffer;
            // 分析处理
            auto pos = inbuffer.find(LINE_SEP);
            if (pos == std::string::npos)
                continue;
            std::string text_len_string = inbuffer.substr(0, pos);
            int text_len = std::stoi(text_len_string);
            int total_len = text_len_string.size() + 2 * LINE_SEP_LEN + text_len;

            std::cout << "处理前#inbuffer: \n" << inbuffer << std::endl;

            if (inbuffer.size() < total_len)
            {
                std::cout << "你输入的消息,没有严格遵守我们的协议,正在等待后续的内容, continue" << std::endl;
                continue;
            }

            // 至少有一个完整的报文
            *text = inbuffer.substr(0, total_len);
            inbuffer.erase(0, total_len);

            std::cout << "处理后#inbuffer:\n " << inbuffer << std::endl;

            break;
        }
        else
            return false;
    }
    return true;
}

calServer.hpp

#pragma once

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "log.hpp"
#include "protocol.hpp"

namespace server
{
    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR,
        LISTEN_ERR
    };

    static const uint16_t gport = 8080;
    static const int gbacklog = 5;
    typedef std::function func_t;

    void handlerEntery(int sock, func_t func)
    {
        std::string inbuffer;
        while (true)
        {
            std::string req_text, req_str;
            if (!recvPackage(sock, inbuffer, &req_text))
                return;
            std::cout << "带报头的请求:\n" << req_text << std::endl;
            if (!deLength(req_text, &req_str))
                return;
            std::cout << "去掉报头的正文:\n" << req_str << std::endl;
            
            Request req;
            if (!req.deserialize(req_str))
                return;

            Response resp;
            func(req, resp); 

            std::string resp_str;
            resp.serialize(&resp_str);

            std::cout << "计算完成, 序列化响应: " <<  resp_str << std::endl;

            std::string send_string = enLength(resp_str);
            std::cout << "构建完成完整的响应\n" <<  send_string << std::endl;
        
            send(sock, send_string.c_str(), send_string.size(), 0); 
        }
    }

    class CalServer
    {
    public:
        CalServer(const uint16_t &port = gport) : _listensock(-1), _port(port)
        {
        }
        void initServer()
        {
            // 1. 创建socket文件套接字对象
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock < 0)
            {
                exit(SOCKET_ERR);
            }

            // 2. bind绑定自己的网络信息
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = INADDR_ANY;
            if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                exit(BIND_ERR);
            }

            // 3. 设置socket 为监听状态
            if (listen(_listensock, gbacklog) < 0) 
            {
                exit(LISTEN_ERR);
            }
        }
        void start(func_t func)
        {
            for (;;)
            {
                // 4. server 获取新链接
                // sock, 和client进行通信的fd
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                int sock = accept(_listensock, (struct sockaddr *)&peer, &len);
                if (sock < 0)
                {
                    continue;
                }

                //多进程版
                pid_t id = fork();
                if (id == 0) 
                {
                    close(_listensock);
                    handlerEntery(sock, func);
                    close(sock);
                    exit(0);
                }
                close(sock);
                pid_t ret = waitpid(id, nullptr, 0);
            }
        }
        ~CalServer() {}

    private:
        int _listensock; 
        uint16_t _port;
    };

} 

calServer.cc

#include "calServer.hpp"
#include 

using namespace server;
using namespace std;

static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}

bool cal(const Request &req, Response &resp)
{
    resp.exitcode = OK;
    resp.result = OK;

    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.exitcode = DIV_ZERO;
        else
            resp.result = req.x / req.y;
    }
    break;
    case '%':
    {
        if (req.y == 0)
            resp.exitcode = MOD_ZERO;
        else
            resp.result = req.x % req.y;
    }
    break;
    default:
        resp.exitcode = OP_ERROR;
        break;
    }

    return true;
}


int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);

    unique_ptr tsvr(new CalServer(port));
    tsvr->initServer();
    tsvr->start(cal);
    return 0;
}

 calClient.hpp

#pragma once

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "protocol.hpp"

#define NUM 1024

class CalClient
{
public:
    CalClient(const std::string &serverip, const uint16_t &serverport)
        : _sock(-1), _serverip(serverip), _serverport(serverport)
    {
    }
    void initClient()
    {
        // 创建socket
        _sock = socket(AF_INET, SOCK_STREAM, 0);
        if (_sock < 0)
        {
            std::cerr << "socket create error" << std::endl;
            exit(2);
        }
    }
    void start()
    {
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(_serverport);
        server.sin_addr.s_addr = inet_addr(_serverip.c_str());

        if (connect(_sock, (struct sockaddr *)&server, sizeof(server)) != 0)
        {
            std::cerr << "socket connect error" << std::endl;
        }
        else
        {
            std::string line;
            std::string inbuffer;
            while (true)
            {
                std::cout << "mycal>>> ";
                std::getline(std::cin, line);  
                Request req = ParseLine(line); 
                std::string content;
                req.serialize(&content);
                std::string send_string = enLength(content);
                std::cout << "sendstring:\n" << send_string << std::endl;
                send(_sock, send_string.c_str(), send_string.size(), 0); 

                std::string package, text;
                //  "content_len"\r\n"exitcode result"\r\n
                if (!recvPackage(_sock, inbuffer, &package))
                    continue;
                if (!deLength(package, &text))
                    continue;
                // "exitcode result"
                Response resp;
                resp.deserialize(text);
                std::cout << "exitCode: " << resp.exitcode << std::endl;
                std::cout << "result: " << resp.result << std::endl;
            }
        }
    }
    Request ParseLine(const std::string &line)
    {
        // 建议版本的状态机!
        //"1+1" "123*456" "12/0"
        int status = 0; // 0:操作符之前,1:碰到了操作符 2:操作符之后
        int i = 0;
        int cnt = line.size();
        std::string left, right;
        char op;
        while (i < cnt)
        {
            switch (status)
            {
            case 0:
            {
                if(!isdigit(line[i]))
                {
                    op = line[i];
                    status = 1;
                }
                else left.push_back(line[i++]);
            }
            break;
            case 1:
                i++;
                status = 2;
                break;
            case 2:
                right.push_back(line[i++]);
                break;
            }
        }
        std::cout << std::stoi(left)<<" " << std::stoi(right) << " " << op << std::endl;
        return Request(std::stoi(left), std::stoi(right), op);
    }
    ~CalClient()
    {
        if (_sock >= 0)
            close(_sock);
    }

private:
    int _sock;
    std::string _serverip;
    uint16_t _serverport;
};

calClient.cc 

#include "calClient.hpp"
#include 

using namespace std;

static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " serverip serverport\n\n";
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    string serverip = argv[1];
    uint16_t serverport = atoi(argv[2]);

    unique_ptr tcli(new CalClient(serverip, serverport));
    tcli->initClient();
    tcli->start();
    return 0;
}

你可能感兴趣的:(Linux,网络协议,TCP)