您现在的位置是:首页 >技术杂谈 >【Linux】通过网络版计算器来认识协议网站首页技术杂谈
【Linux】通过网络版计算器来认识协议
简介【Linux】通过网络版计算器来认识协议
🌠 作者:@阿亮joy.
🎆专栏:《学会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 "
"
#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] = '