Linux之网络学习第四部分:模拟计算器+序列化与反序列化

本章作用主要为承上启下,一方面训练tcp使用的流程接口,另一方面为下一章介绍http做铺垫

模拟计算器无序列化版

我们要自己定义一个协议,client && server都要遵守!
业务逻辑存在Protocol.hpp中,即请求消息的结构类型request_t,响应消息的结构类型response_t。
server_cal.cpp

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <pthread.h>
#include <unistd.h>
#include "Sock.hpp"
#include "Protocol.hpp"
using namespace std;

void *HandlerRequest(void *argc)
{
    int sock = *(int *)argc;
    delete (int *)argc;
    pthread_detach(pthread_self());

    // version1 原生方法,没有明显的序列化和反序列化的过程
    // 业务逻辑, 做一个短服务
    // request -> 分析处理 -> 构建response -> sent(response)->close(sock)
    // 1. 读取请求
    request_t req;
    ssize_t s = read(sock, &req, sizeof(req));
    if (s == sizeof(req))
    {
        //读取到了完整的请求,待定
        // req.x , req.y, req.op

        // 2. 分析请求 && 3. 计算结果
        // 4. 构建响应,并进行返回
        response_t resp = {0, 44};
        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;//代表除0
            else
                resp.result = req.x / req.y;
            break;
        case '%':
            if (req.y == 0)
                resp.code = -2;//代表%0
            else
                resp.result = req.x % req.y;
            break;
        default:
            resp.code = -3;//请求异常
            break;
        }
        cout << "request: " << req.x << req.op << req.y << endl;
        write(sock, &resp, sizeof(resp));
        cout << "服务结束" << endl;
    }
    // 5.关闭连接
    close(sock);
}

void Usage()
{
    cout << "格式有误,应当: ./server_cal + port  " << endl;
}

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

    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, port);
    Sock::Listen(listen_sock);

    for (;;)
    {
        int newsock = Sock::Accept(listen_sock);
        //连接成功开始服务
        if (newsock >= 0)
        {
            cout << "与客户连接成功" << endl;
            pthread_t tid;
            int *parm = new int(newsock);
            pthread_create(&tid, nullptr, HandlerRequest, (void *)parm);
        }
    }

    return 0;
}

client_cal.cpp

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "Sock.hpp"
#include "Protocol.hpp"
using namespace std;

void Usage()
{
    cout << "格式有误,应当: ./client_cal + ip + port  " << endl;
}

int main(int argv, char *argc[])
{
    if (argv != 3)
    {
        Usage();
        return 1;
    }

    int sock = Sock::Socket();
    Sock::Connect(sock, argc[1], atoi(argc[2]));

    // 业务逻辑
    request_t req;
    memset(&req, 0, sizeof(req));
    cout << "Please Enter Data One# ";
    cin >> req.x;
    cout << "Please Enter Data Two# ";
    cin >> req.y;
    cout << "Please Enter operator# ";
    cin >> req.op;

    write(sock, &req, sizeof(req));

    response_t resp;
    ssize_t s = read(sock, &resp, sizeof(resp));
    if (s == sizeof(resp))
    {
        cout << "code[0:success]: " << resp.code << endl;
        cout << "result: " << resp.result << std::endl;
    }
    else
    {
        cout << "read faile" << endl;
    }

    return 0;
}

Sock.hpp

#pragma once
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
using namespace std;

class Sock
{
public:
    static int Socket()
    {
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0)
        {
            cerr << "sock create fail" << errno << endl;
            exit(2);
        }
        return sock;
    }

    static void Bind(int sock, uint16_t port)
    {
        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(sock, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            cerr << "bind erro" << errno << endl;
            exit(3);
        }
    }

    static void Listen(int sock)
    {
        const int back_log = 5;
        if (listen(sock, back_log) < 0)
        {
            cerr << "listen erro" << errno << endl;
            exit(4);
        }
    }

    static int Accept(int sock)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int fd = accept(sock, (struct sockaddr *)&peer, &len);
        if(fd >= 0)
            return fd;
        return -1;
    }

    static void Connect(int sock, std::string ip, uint16_t port)
    {
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));

        server.sin_family = AF_INET;
        server.sin_port = htons(port);
        server.sin_addr.s_addr = inet_addr(ip.c_str());

        if(connect(sock, (struct sockaddr*)&server, sizeof(server)) == 0)
        {
            cout << "Connect Success!" << endl;
        }
        else
        {
            cout << "Connect Failed!" << endl;
            exit(5);
        }
    }

};

Protocol.hpp

#pragma once
#include <iostream>
#include <string>
using namespace std;

// 定制协议的过程,目前就是定制结构化数据的过程
// 请求格式
// 我们自己定义的协议,client && server 都必须遵守! 这就叫做自定义协议

typedef struct Request
{
    int x;
    int y;
    char op;
}request_t;

typedef struct Response
{
    int code;
    int result;
}response_t;

Makefile

.PHONY:all
all:server_cal client_cal

server_cal:server_cal.cpp
	g++ -o $@ $^ -std=c++11 -lpthread

client_cal:client_cal.cpp
	g++ -o $@ $^ -std=c++11 -lpthread

.PHONY:clean
clean:
	rm -rf client_cal server_cal

在这里插入图片描述

上述没有序列化的版本,只是通过原生结构体的形式发送二进制的方式进行通信的,看似可行,只不过是少数情况而已。
在现实情况中如果软件版本迭代了,比如内存对齐规则改变等,因此如果传过来的2进制数据一个字节发生了变化就造成无法通信。
所以这种通信的方式是不科学的,因此就需要序列化与反序列化!!

序列化与反序列化组件的使用(了解即可)

两种序列化格式
在这里插入图片描述
在这里插入图片描述
反序列化
在这里插入图片描述

模拟计算器序列化与反序列化版

server_cal.cpp

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <pthread.h>
#include <unistd.h>
#include "Sock.hpp"
#include "Protocol.hpp"
using namespace std;

void *HandlerRequest(void *argc)
{
    int sock = *(int *)argc;
    delete (int *)argc;
    pthread_detach(pthread_self());

    // version1 原生方法,没有明显的序列化和反序列化的过程
    // 业务逻辑, 做一个短服务
    // request -> 分析处理 -> 构建response -> sent(response)->close(sock)
    // 1. 读取请求 + 反序列化

    char buffer[1024];
    memset(&buffer, 0, sizeof(buffer));
    ssize_t s = read(sock, &buffer, sizeof(buffer));
    request_t req;
    if (s > 0)
    {
        buffer[s] = 0;
        cout << "获取到新的请求 " << buffer << endl;
        DeserializationRequest(buffer, req);
    }

    //读取到了完整的请求,待定
    // req.x , req.y, req.op

    // 2. 分析请求 && 3. 计算结果
    // 4. 构建序列化响应,并进行返回
    response_t resp = {0, 44};
    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; //代表除0
        else
            resp.result = req.x / req.y;
        break;
    case '%':
        if (req.y == 0)
            resp.code = -2; //代表%0
        else
            resp.result = req.x % req.y;
        break;
    default:
        resp.code = -3; //请求异常
        break;
    }
    cout << "request: " << req.x << req.op << req.y << endl;
    string send_string = SerializeResponse(resp);
    write(sock, send_string.c_str(), send_string.size());
    cout << "服务结束 " << send_string << endl;

    // 5.关闭连接
    close(sock);
}

void Usage()
{
    cout << "格式有误,应当: ./server_cal + port  " << endl;
}

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

    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, port);
    Sock::Listen(listen_sock);

    for (;;)
    {
        int newsock = Sock::Accept(listen_sock);
        //连接成功开始服务
        if (newsock >= 0)
        {
            cout << "与客户连接成功" << endl;
            pthread_t tid;
            int *parm = new int(newsock);
            pthread_create(&tid, nullptr, HandlerRequest, (void *)parm);
        }
    }

    return 0;
}

client_cal.cpp

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "Sock.hpp"
#include "Protocol.hpp"
using namespace std;

void Usage()
{
    cout << "格式有误,应当: ./client_cal + ip + port  " << endl;
}

int main(int argv, char *argc[])
{
    if (argv != 3)
    {
        Usage();
        return 1;
    }

    int sock = Sock::Socket();
    Sock::Connect(sock, argc[1], atoi(argc[2]));

    // 业务逻辑
    request_t req;
    memset(&req, 0, sizeof(req));
    cout << "Please Enter Data One# ";
    cin >> req.x;
    cout << "Please Enter Data Two# ";
    cin >> req.y;
    cout << "Please Enter operator# ";
    cin >> req.op;

    string send_string = SerializeRequest(req);
    write(sock, send_string.c_str(), send_string.size());

    char buffer[1024];
    memset(&buffer, 0, sizeof(buffer));
    ssize_t s = read(sock, &buffer, sizeof(buffer));
    if (s > 0)
    {
        buffer[s] = 0;
        string str = buffer;
        response_t resp;
        DeserializationResponse(str, resp);
        cout << "code[0:success]: " << resp.code << endl;
        cout << "result: " << resp.result << std::endl;
    }
    else
    {
        cout << "read faile" << endl;
    }

    return 0;
}

Sock.hpp

#pragma once
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
using namespace std;

class Sock
{
public:
    static int Socket()
    {
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0)
        {
            cerr << "sock create fail" << errno << endl;
            exit(2);
        }
        return sock;
    }

    static void Bind(int sock, uint16_t port)
    {
        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(sock, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            cerr << "bind erro" << errno << endl;
            exit(3);
        }
    }

    static void Listen(int sock)
    {
        const int back_log = 5;
        if (listen(sock, back_log) < 0)
        {
            cerr << "listen erro" << errno << endl;
            exit(4);
        }
    }

    static int Accept(int sock)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int fd = accept(sock, (struct sockaddr *)&peer, &len);
        if(fd >= 0)
            return fd;
        return -1;
    }

    static void Connect(int sock, std::string ip, uint16_t port)
    {
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));

        server.sin_family = AF_INET;
        server.sin_port = htons(port);
        server.sin_addr.s_addr = inet_addr(ip.c_str());

        if(connect(sock, (struct sockaddr*)&server, sizeof(server)) == 0)
        {
            cout << "Connect Success!" << endl;
        }
        else
        {
            cout << "Connect Failed!" << endl;
            exit(5);
        }
    }

};

Protocol.hpp

#pragma once
#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>
using namespace std;

// 定制协议的过程,目前就是定制结构化数据的过程
// 请求格式
// 我们自己定义的协议,client && server 都必须遵守! 这就叫做自定义协议

typedef struct Request
{
    int x;
    int y;
    char op;
} request_t;

typedef struct Response
{
    int code;
    int result;
} response_t;

//序列化请求 : request_t --> string
string SerializeRequest(const request_t &req)
{
    Json::Value root; //可以承装任何对象, json是一种kv式的序列化方案
    root["datax"] = req.x;
    root["datay"] = req.y;
    root["op"] = req.op;

    // Json::StyledWriter writer;
    Json::FastWriter writer;
    // write的返回值是经过序列化后的string类型的字符串
    string res = writer.write(root);
    return res;
}
//反序列化请求 :string --> request_t
void DeserializationRequest(const string &json_string, request_t &out)
{
    Json::Reader read;
    Json::Value root;

    read.parse(json_string, root);
    out.x = root["datax"].asInt();
    out.y = root["datay"].asInt();
    out.op = (char)root["op"].asInt();
}

//序列化响应 : response_t --> string
string SerializeResponse(const response_t &resp)
{
    Json::Value root;
    root["code"] = resp.code;
    root["result"] = resp.result;

    Json::FastWriter writer;
    string res = writer.write(root);
    return res;
}
//反序列化响应  :string --> response_t
void DeserializationResponse(const string &json_string, response_t &out)
{
    Json::Reader read;
    Json::Value root;

    read.parse(json_string, root);
    out.code = root["code"].asInt();
    out.result = root["result"].asInt();
}

在这里插入图片描述
接下来请看第五部分

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值