【Linux Network】网络版计算器

news2024/12/23 14:45:21

目录

实验目标:

源代码:

实验结果:



Linux网络编程✨ 

实验目标:

制作一个应用层的简易版的计算器(加、减、乘、除、取余);

源代码:

  • makefile
.PHONY:all
all:CalClient CalServer

CalClient:CalClient.cc
	g++ -o $@ $^ -std=c++11 -ljsoncpp
CalServer:CalServer.cc
	g++ -o $@ $^ -std=c++11 -lpthread -ljsoncpp

.PHONY:clean
clean:
	rm CalClient CalServer -rf
  • Protocol.hpp
#pragma once

#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>

using namespace std;

// 定制协议的过程,目前就是定制结构化数据的过程
// 请求格式
// 我们自己定义的协议,client && server 都必须遵守! 这就叫做自定义协议
typedef struct request
{
    int x;  //10
    int y;  //0
    char op; // '/'      "+-*/%"
} request_t; //10/0

// 响应格式
typedef struct response 
{
    int code;   // server运算完毕的计算状态: code(0:success), code(-1: div 0) ...
    int result; // 计算结果, 能否区分是正常的计算结果,还是异常的退出结果
}response_t;

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

    //FastWriter, StyledWriter
    // Json::StyledWriter writer;
    Json::FastWriter writer;
    std::string json_string = writer.write(root);
    return json_string;
}

// string -> request_t
void DeserializeRequest(const std::string &json_string, request_t &out)
{
    //反序列化
    Json::Reader reader;
    Json::Value root;

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

std::string SerializeResponse(const response_t &resp)
{
    Json::Value root;
    root["code"] = resp.code;
    root["result"] = resp.result;

    Json::FastWriter writer;
    std::string res = writer.write(root);

    return res;
}

void DeserializeResponse(const std::string &json_string, response_t &out)
{
        //反序列化
    Json::Reader reader;
    Json::Value root;

    reader.parse(json_string, root);
    out.code = root["code"].asInt();
    out.result = root["result"].asInt();
}
  • Sock.hpp
#pragma once

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

using namespace std;

class Sock
{
public:
    static int Socket()
    {
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0)
        {
            cerr << "socket error" << 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 error!" << endl;
            exit(3);
        }
    }

    static void Listen(int sock)
    {
        if (listen(sock, 5) < 0)
        {
            cerr << "listen error !" << 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);
        }
    }
};
  • CalServer.cc
#include <pthread.h>
#include "Protocol.hpp"
#include "Sock.hpp"

static void Usage(string proc)
{
    cout << "Usage: " << proc << " port" << endl;
    exit(1);
}

void *HandlerRequest(void *args)
{
    int sock = *(int *)args;
    delete (int *)args;

    pthread_detach(pthread_self());

    // version1 原生方法,没有明显的序列化和反序列化的过程
    // 业务逻辑, 做一个短服务
    // request -> 分析处理 -> 构建response -> sent(response)->close(sock)
    // 1. 读取请求
    char buffer[1024];
    request_t req;
    ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
    if (s > 0)
    {
        buffer[s] = 0;
        cout << "get a new request: " << buffer << endl;
        std::string str = buffer;
        DeserializeRequest(str, req); //反序列化请求

        // 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, 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; //代表除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));
        std::string send_string = SerializeResponse(resp);   //序列化之后的字符串
        write(sock, send_string.c_str(),send_string.size());     
        cout << "服务结束: " << send_string << endl;
        // }
    }

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

// ./CalServer port
int main(int argc, char *argv[])
{
    if (argc != 2)
        Usage(argv[0]);
    uint16_t port = atoi(argv[1]);

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

    for (;;)
    {
        int sock = Sock::Accept(listen_sock);
        if (sock >= 0)
        {
            cout << "get a new client..." << endl;
            int *pram = new int(sock);
            pthread_t tid;
            pthread_create(&tid, nullptr, HandlerRequest, pram);
        }
    }

    return 0;
}
  • CalClient.cc
#include "Protocol.hpp"
#include "Sock.hpp"

void Usage(string proc)
{
    cout << "Usage: " << proc << " server_ip server_port" << endl;
}
// ./CalClient server_ip server_port
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    int sock = Sock::Socket();
    Sock::Connect(sock, argv[1], atoi(argv[2]));

    // while (true)
    // {
    // 业务逻辑
    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;

    std::string json_string = SerializeRequest(req);

    // ssize_t s = write(sock, &req, sizeof(req));
    ssize_t s = write(sock, json_string.c_str(), json_string.size());

    char buffer[1024];
    s = read(sock, buffer, sizeof(buffer) - 1);
    if (s > 0)
    {
        response_t resp;
        buffer[s] = 0;
        std::string str = buffer;
        DeserializeResponse(str, resp);

        cout << "code[0:success]: " << resp.code << endl;
        cout << "result: " << resp.result << std::endl;
    }

    return 0;
}

实验结果:

这个CS模式的在线版本计算器,本质上就是一个应用层(TCP/IP四层协议)网络服务;

对应OIS七层协议的会话层、表示层、应用层都是我们自己写的;

基本通信代码(套接字)——> 会话层;

序列和反序列化(使用 json 组件完成)——> 表示层;

业务逻辑(请求、结果格式等约定是我们自己做的)——> 应用层;

 如果上述文章对您有所帮助的话,还请点赞👍,收藏😉,关注🎈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/518454.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

国产仪器 6592A便携式高精度光伏电池伏安特性测试仪

6592A便携式高精度光伏电池伏安特性测试仪&#xff0c;主要用于室外太阳能电池阵列/组件/电池片伏安特性测试&#xff0c;能够方便、快速的测试太阳能电池阵列/组件/电池片在自然光照下的工作特性&#xff0c;可为太阳能电站设计、验收、维护提供测试保障&#xff0c;是电站建设…

sql进阶—— 查询重复数据 查询连续数据

目录 sql语句查询重复的数据 MYSQL 连续性问题求解 binlog 索引 sql语句查询重复的数据 查找所有重复 [标题] 的记录&#xff1a;SELECT * FROM t_info a WHERE ((SELECT COUNT(*) FROM t_info WHERE Title a.Title) > 1) ORDER BY Title DESC删除重复记录1。删除全部…

(9)Qt---网络编程(半双工通信)

目录 1. 复习 1.1 UDP 与TCP 1.2 IP地址与端口号 2. 前期准备 3. 编程内容 1. 复习 1.1 UDP 与TCP UDP TCP 协议相同点&#xff1a;都存在于传输层 TCP&#xff08;即传输控制协议&#xff09;&#xff1a; 是一种面向连接的传输层协议&#xff0c;它能提供高可靠性通信(即…

牛客网面试必刷:BM18 二维数组中的查找

牛客网面试必刷&#xff1a;BM18 二维数组中的查找 前言一、解法1&#xff1a;逐行使用二分搜索二、解法2&#xff1a;线性搜索&#xff08;推荐&#xff09; 前言 二分查找常见的是在一维数据中进行&#xff0c;在我的上一篇文章中已经有介绍。 一维数组查找&#xff1a;BM1…

STM32F4_DHT11数字温湿度传感器

目录 前言 1. DHT11简介 2. DHT11数据结构 3. DHT11的传输时序 3.1 DHT11开始发送数据流程 3.2 主机复位信号和DHT11响应信号 3.3 数字 “0” 信号表示方法 3.4 数字 “1” 信号表示方法 4. 硬件分析 5. 实验程序详解 5.1 main.c 5.2 DHT11.c 5.3 DHT11.h 前言 DH…

c#笔记-泛型

泛型方法 假设我们要编写一个方法&#xff0c;它可以获取任意类型数组中的最大值&#xff0c; 并返回该值。我们可能会这样写&#xff1a; static int GetMax(int[] array) {Array.Sort(array);return array[array.Length - 1]; }这个方法可以实现我们的需求&#xff0c;但是…

【虚拟机】VMware16保姆级安装教程

大家好&#xff0c;我是雷工&#xff01; 工作中需要用到各种各样的工控软件&#xff0c;有时候甚至需要不同版本的软件&#xff0c;但频繁装卸软件比较麻烦&#xff0c;而且像WinCC和博图软件对系统要求比较严格&#xff0c;卸载重装可能就出问题&#xff0c;此时就不得不重装…

MODBUS通讯程序设计流程图

MODBUS通讯一般来说是基于RS485电平的通讯&#xff0c;RS485是半双工&#xff0c;很多单片机工程师做的通讯不是稳定&#xff0c;主要原因是流程没有掌控好。 我以前也犯过和他们一样错误&#xff0c;觉得很容易。在QQ群里&#xff0c;有位老工程师和大家一样犯了同样的毛病。…

宣布!BNB Greenfield 测试网上的部署

我们很高兴地宣布&#xff0c;4EVERLAND Hosting 现在支持在 BNB Greenfield Testnet 上部署&#xff0c;为开发和托管去中心化应用程序 (dApp) 提供更具可扩展性和安全性的选项。 这种集成使项目团队能够轻松且经济地利用 4EVERLAND 在去中心化托管方面的尖端技术和专业知识&…

No module named ‘torch_geometric‘解决办法——PyG(PyTorch Geometric)安装教程

1、查询已安装的torch版本 一定要先查询自己的torch版本&#xff0c;也不同版本的依赖跟库都是不一样的。 import torch print("torch版本&#xff1a;",torch.__version__)输出结果&#xff1a; 2、安装依赖 2.1 进入官网下载依赖。 官网下载链接&#xff1a;…

word查找与替换

比如我有一个需求&#xff1a;将word中符号和中文为全角&#xff0c;但英文字符和数字为半角。 这种我们应该怎么办&#xff1f; 可以通过查找和替换解决。 按ctrl H即可看到查找替换框&#xff0c;注意word中查找替换不是ctrl F 查找 功能&#xff1a;在以下选项中查找 我们…

WiFi(Wireless Fidelity)基础(十)

目录 一、基本介绍&#xff08;Introduction&#xff09; 二、进化发展&#xff08;Evolution&#xff09; 三、PHY帧&#xff08;&#xff08;PHY Frame &#xff09; 四、MAC帧&#xff08;MAC Frame &#xff09; 五、协议&#xff08;Protocol&#xff09; 六、安全&#x…

Springboot +Flowable,各种历史信息如何查询(二)

一.简介 正在执行的流程信息是保存在以 ACT_RU_ 为前缀的表中&#xff0c;执行完毕的流程信息则保存在以 ACT_HI_ 为前缀的表中&#xff0c;也就是流程历史信息表。 假设有一个流程&#xff0c;流程图如下&#xff1a; 当这个流程执行完毕后&#xff0c;以 ACT_RU_ 为前缀的…

【ubuntu】alias命令

1 alias的作用 alias允许用户为命令创建简单的名称或缩写。如果需要在窗口输入复杂且较多的命令时&#xff0c;alias就可以减轻用户的记忆负担&#xff0c;发挥作用啦。 2 语法 &#xff08;1&#xff09;简单命令 alias [name”value”] 举例&#xff1a;alias ppwd 命令…

学习网络安全有哪些就业方向?网络安全就业前景!

在当下&#xff0c;网络安全是非常重要的&#xff0c;与我们的生活息息相关&#xff0c;因此网络安全课程也成为了热门技术&#xff0c;引发众多人学习。那么学习网络安全有哪些就业方向?我想很多人都不太了解&#xff0c;接下来我们一起来看看吧。 首先说一下什么是网络安全…

科罗拉多州立大学发布CSU-MLP模型,用随机森林预测中期恶劣天气

本文首发自 HyperAI超神经微信公众号~ 内容一览&#xff1a;近期&#xff0c;来自美国科罗拉多州立大学与 SPC 的相关学者联合发布了一个基于随机森林的机器学习模型 CSU-MLP&#xff0c;该模型能够对中期 (4-8天) 范围内恶劣天气进行准确预报。目前该成果刊已发表在《Weather …

进销存是什么意思?值得推荐的进销存软件有哪些?

在这个消费不断升级、直播带货和电商不断冲击的时代&#xff0c;实体店的生存变得越来越艰难&#xff0c;如何提高门店管理效率、降低管理成本、提升门店客流量&#xff0c;是实体店当下急需解决的几大问题。 进销存软件就是为了帮助实体店解决以上几大难题而设计的&#xff0c…

Xline v0.4.0: 一个用于元数据管理的分布式KV存储

Xline是什么&#xff1f;我们为什么要做Xline&#xff1f; Xline是一个基于Curp协议的&#xff0c;用于管理元数据的分布式KV存储。现有的分布式KV存储大多采用Raft共识协议&#xff0c;需要两次RTT才能完成一次请求。当部署在单个数据中心时&#xff0c;节点之间的延迟较低&a…

对STM32栈的理解Stack_Size EQU 0x00000400

对STM32栈的理解Stack_Size EQU 0x00000400 Stack_Size EQU 0x00000400表示什么意思可以通过查找flash内存的方式定位存储1.flash2.RAM内部 本人主要为个人参考网络及个人总结而来比较&#xff0c;如有雷同请告知&#xff0c;即刻删除 以下引用网上资料 理解堆和栈的区别 &…

RK3588 设备树pinctrl gpio子系统解析,解决GPIO无法正确拉高拉低的问题,RK3588设备树详解

一、RK3588设备树结构 firefly的官方说明文档RK3588gpio系统说明 function {group {rockchip,pin <bank gpio func &ref>;}; };其中&#xff0c;bank是所属的组&#xff0c;Core-3588J 有 5 组 GPIO bank&#xff1a;GPIO0-GPIO4&#xff0c;每组又以 A0-A7, B0-B…