【Linux】通过网络版计算器来认识协议

👉再谈协议👈

协议的概念

网络通信中的协议是指在网络中进行数据传输时遵循的一些规则和标准,用于确保不同设备之间的通信能够顺利进行。协议的本质是软件,它最终是需要通过计算机语言(编码)的方式来表现出来,协议如何编写取决于我们的应用场景。

结构化数据的传输

通行双方在进行网络通信时:

  • 如果想要传输的数据是一个字符串,那么可以直接将这个字符串发送到网络中,此时对端从网络中获取到这个字符串就实现网络通信了。
  • 而如果想要传输的是一些结构化的数据,那么就无法直接将这些数据发送到网络中,需要先进行序列化再发送到网络中,然后对端从网络中获取到序列化的数据,并将这些数据进行反序列化,就可以得到结构化的数据了。

序列化和反序列化

序列化和反序列化是计算机中常见的概念,用于在不同系统或网络之间传输数据或存储数据时进行格式转换。

序列化是指将对象或数据结构转换成字节流的过程,以便于在网络或存储设备上进行传输或存储。在序列化的过程中,会将对象或数据结构的属性或元素逐个转换成二进制格式,并将这些二进制数据组成一个连续的字节流,以便于传输或存储。

反序列化是指将序列化后的字节流转换成对象或数据结构的过程,以便于在程序中进行操作。在反序列化的过程中,会将字节流逐个读取,并将其转换成相应的对象属性或数据结构元素,以便于程序对其进行操作。

在网络通信中,客户端向服务器发送请求时,需要将请求对象序列化成字节流进行传输;服务器收到请求后,需要将接收到的字节流反序列化成请求对象进行处理。

注:序列化和反序列化可以让上层业务和网络传输进行一定程度的解耦。
在这里插入图片描述

👉网络版计算器👈

网络版计算器要实现的功能:我们需要客户端把数据和操作符发给服务器,然后由服务器进行计算,最后再把结果返回给客户端。为了实现这样的网络版计算器,我们就需要进行协议定制。

协议定制

  • 定义结构体来表示我们需要交互的信息,如客服端的请求中需要需要包含两个操作数和一个操作码,服务端的应答中需要包含表示计算结果的状态码和计算结果。
  • 发送数据时将这个结构体按照一个规则序列化成字符串,接收到数据的时候再按照相同的规则把字符串反序列化成结构体。
  • 对要发送的数据进行序列化后,还需要进行添加报头的操作。为什么要添加报头呢?因为添加报头可以解决黏包问题,以确保每次读取数据时读到的都是一个完整的报文。
#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>

#define SPACE " "
#define SPACE_LEN strlen(SPACE)
#define SEP "\r\n"
#define SEP_LEN strlen(SEP)
#define SIZE 1024
#define MYSELF

class Request
{
   
public:
    Request() {
   }

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

    ~Request() {
   }

    std::string Serialize()
    {
   
    #ifdef MYSELF        
        std::string str = std::to_string(_x);
        str += SPACE;
        str += _op;
        str += SPACE;
        str += std::to_string(_y);
        return str;
    #else

    #endif
    }

    bool Deserialize(std::string& str)
    {
   
    #ifdef MYSELF
        size_t left = str.find(SPACE);
        if(left == std::string::npos)
            return false;
        size_t right = str.rfind(SPACE);
        if(right == std::string::npos)
            return false;

        if(left + SPACE_LEN >= str.size())
            return false;

        _x = atoi(str.substr(0, left).c_str());
        _y = atoi(str.substr(right + SPACE_LEN).c_str());
        _op = str[left + SPACE_LEN];
    #else

    #endif
    }

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

class Response
{
   
public:
    Response() {
   }

    Response(int code, int ret, int x, int y, char op)
        : _code(code)
        , _ret(ret)
        , _x(x)
        , _y(y)
        , _op(op)
    {
   }

    ~Response() {
   }

    std::string Serialize()
    {
   
    #ifdef MYSELF
        std::string str = std::to_string(_code);
        str += SPACE;
        str += std::to_string(_ret);
        return str;
    #else

    #endif
    }

    bool Deserialize(const std::string& str)
    {
   
    #ifdef MYSELF
        size_t pos = str.find(SPACE);
        if(pos == std::string::npos)
            return false;
        _code = atoi(str.substr(0, pos).c_str());
        _ret = atoi(str.substr(pos + SPACE_LEN).c_str());
        return true;
    #else

    #endif
    }

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

bool Recv(int sock, std::string* out)
{
   
    char buffer[SIZE];
    ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
    if(s > 0)
    {
   
        buffer[s] = '\0';
        *out += buffer;
        return true;
    }
    else if(s == 0)
    {
   
        std::cout << "Client Quit!" << std::endl;
        return false;
    }
    else
    {
   
        std::cout << "Recv Error!" << std::endl;
        return false;
    }
}

void Send(int sock, const std::string& str)
{
   
    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)
{
   
    size_t pos = buffer.find(SEP);
    if(pos == std::string::npos)
        return "";
    
    int size = atoi(buffer.substr(0, pos).c_str());
    int leftSize = buffer.size() - pos - 2 * SEP_LEN;
    if(leftSize >= size)
    {
   
        // 至少有一个完整的报文
        buffer.erase(0, pos + SEP_LEN);
        std::string 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值