建设网站选多大的空间合适,长春市招标网,黑龙江网站建设费用,如何把网页做成响应式的#x1f941;作者#xff1a; 华丞臧. #x1f4d5;专栏#xff1a;【网络】 各位读者老爷如果觉得博主写的不错#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方#xff0c;欢迎在评论区指出。 推荐一款刷题网站 #x1f449; LeetCode刷题网站 文章… 作者 华丞臧. 专栏【网络】 各位读者老爷如果觉得博主写的不错请诸位多多支持(点赞收藏关注)。如果有错误的地方欢迎在评论区指出。 推荐一款刷题网站 LeetCode刷题网站 文章目录一、应用层二、网络版本计算器2.1 协议定制2.2 协议实现encodedecode定制请求 -- Requset定制响应 -- Response输入参数读取测试2.3 使用第三方库测试一、应用层
程序员写的一个个解决实际问题满足日常需求的网络程序都是在应用层的。协议是一种“约定”socket api的接口在读写数据时都是按“字符串”的方式来发送接收的。如果我们要传输一些“结构化数据”能直接传输吗肯定是不能直接传输的结构化数据存在一些问题比如C语言中结构体需要结构体对齐C中类同样需要对齐会浪费网络资源并且不同操作系统的大小端在不同编译器下可能不同。
所以对于需要传输结构化数据我们可以进行约定
定义结构体来表示我们需要交互的信息发送数据时将这个结构体按照约定的规则转换成字符串接收到数据的时候再按照相同的规则把字符串转回结构体。序列化将结构体数据转换成字符串的过程。反序列化将字符串数据转换回结构化数据的过程。
二、网络版本计算器
在进行结构化数据的网络通信时需要将数据序列化成字符串再发送给对方但是对方并不知道传输数据的长度也不知道如何从字符串的数据中读取结构化的数据因此在进行网络通信之间通信的双发需要进行约定比如约定如何确定序列化数据的长度以及用什么格式来反序列化字符串。 在这里我们先手写一个定制协议约定如下
计算器格式通常是12、 2*3、9/3序列化字符串首部几个字节表示长度有效载荷通过空格隔开表示长度的报头和有效载荷通过\r\n隔开报文之间也是用\r\n隔开
2.1 协议定制 客户端和服务端代码请看序列化和反序列化
#pragma once
#include iostream
#include string
#include assert.h#include Log.hpp#define CRLF \r\n
#define CRLF_LEN strlen(CRLF)
#define SPACE
#define SPACE_LEN strlen(SPACE)
#define OPS -*/%// encode,对整个序列化之后的字符串进行添加长度
//strlen\r\nXXXXXX\r\n
std::string encode(std::string in, uint32_t len)
{}// decode整个序列化之后的字符串进行提取长度
// 1. 必须具有完整的长度
// 2. 必须具有和len相符合的有效载荷
// 我们才返回有效载荷和len
// 否则我们就是一个检测函数
// 9\r\n100 200\r\n 9\r\n112 / 200\r\n
std::string decode(std::string in, uint32_t *len)
{}// 定制请求
class Request
{
public:Request(){}~Request(){}int getX(){ return x_;}int getY(){ return y_;}char getOp(){ return op_;}// 序列化void serialization(std::string *out){}// 反序列化bool deserialization(std::string in){}void debug(){}
private:// 需要计算的数据int x_; int y_;// 需要计算的种类, - * / %char op_;
};// 定制响应
class Response
{
public:Response():result_(0),exitCode_(0){}~Response(){}// 序列化void serialization(std::string *out){}// 反序列化bool deserialization(std::string in){}
public:// 退出状态0标识运算结果合法非0标识运算结果是非法的int exitCode_; // 运算结果int result_;
};bool makeReuquest(const std::string str, Request *req)
{}2.2 协议实现
encode
按照约定需要在序列化之后的字符串首部加上有效载荷的长度并用\r\n隔开并且结尾也需要加上\r\n。
// encode,对整个序列化之后的字符串进行添加长度
//strlen\r\nXXXXXX\r\n
std::string encode(std::string in, uint32_t len)
{// exitCode_ result_// len\r\n exitCode_ result_\r\nstd::string encodeStr std::to_string(len);encodeStr CRLF;encodeStr in;encodeStr CRLF;//std::cout debug-encode- encodeStr std::endl;return encodeStr;
}decode
第一步要先提取有效载荷的长度再根据长度检查字符串中是否含有一个完整的有效载荷如果具有完整的有效载荷可以通过长度来提取有效载荷。注意如果需要进行多次提取需要将读取出的字符串删除。
// decode整个序列化之后的字符串进行提取长度
// 1. 必须具有完整的长度
// 2. 必须具有和len相符合的有效载荷
// 我们才返回有效载荷和len
// 否则我们就是一个检测函数
// 9\r\n100 200\r\n 9\r\n112 / 200\r\n
std::string decode(std::string in, uint32_t *len)
{assert(len);// 1. 确认是否是一个包含len的有效字符串*len 0;size_t pos in.find(CRLF);if(pos std::string::npos)return ;// 2. 提取长度std::string strLen in.substr(0, pos);int intLen atoi(strLen.c_str());// 3. 确认有效载荷也是符合要求的int surplus in.size() - 2 * CRLF_LEN - pos;if(surplus intLen){return ;}// 4. 确认有完整的报文结构std::string package in.substr(pos CRLF_LEN, intLen);*len intLen;// 5. 将当前报文完整的从in中全部移除掉int remLen strLen.size() 2 *CRLF_LEN package.size();in.erase(0, remLen);// 6. 正常返回return package;
}定制请求 – Requset
定制请求就是给请求方一个存放结构化数据的空间请求方可以通过定制好的协议进行序列化得到字符串然后就可以与服务端进行网络通信了Request中的反序列化通常是给服务端提取请求方的结构化数据所以服务端可以根据结构化数据向请求方进行响应。
序列化
序列化出的数据属于有效载荷因此按照协议需要使用空格隔开
反序列化
按照协议规定结构化的数据在字符串中是通过空格隔开的所以我们可以根据字符串中的两个空格将数据结构化提取出来。
#define CRLF \r\n
#define CRLF_LEN strlen(CRLF)
#define SPACE
#define SPACE_LEN strlen(SPACE)// 定制请求
class Request
{
public:Request(){}~Request(){}int getX(){ return x_;}int getY(){ return y_;}char getOp(){ return op_;}// 序列化void serialization(std::string *out){// 100 200 // 100 200std::string dateOne std::to_string(x_);std::string dateTwo std::to_string(y_);std::string dateOp std::to_string(op_);*out dateOne;*out SPACE;*out op_;*out SPACE;*out dateTwo;//std::cout *out std::endl;}// 反序列化bool deserialization(std::string in){//std::cout 0 std::endl;// 100 200 size_t spaceOne in.find(SPACE);if(spaceOne std::string::npos) return false;//std::cout 1 std::endl;size_t spaceTwo in.rfind(SPACE);if(spaceTwo std::string::npos) return false;//std::cout 2 std::endl;std::string dateOne in.substr(0, spaceOne);std::string dateTwo in.substr(spaceTwo SPACE_LEN);std::string dateOp in.substr(spaceOne SPACE_LEN, spaceTwo - (spaceOne SPACE_LEN));if(dateOp.size() ! 1) {printf(%d:%c\n, dateOp.size(), dateOp.c_str());return false;}//std::cout 3 std::endl;// 转成内部成员x_ atoi(dateOne.c_str());y_ atoi(dateTwo.c_str());op_ atoi(dateOp.c_str());return true;}void debug(){std::cout x_ x_ std::endl;std::cout y_ y_ std::endl;std::cout op_ op_ std::endl;}
private:// 需要计算的数据int x_; int y_;// 需要计算的种类, - * / %char op_;
};定制响应 – Response
响应是服务端在对客户端的请求提供服务之后给客户端返回的结果所以Response需要能够存放服务结果以及发生错误时的状态码而结果和错误码是数据化结构所以需要序列化之后在传输给客户端客户端在拿到响应后需要进行反序列化拿到结果和状态码。序列胡和反序列化过程与定制请求类似。
#define CRLF \r\n
#define CRLF_LEN strlen(CRLF)
#define SPACE
#define SPACE_LEN strlen(SPACE)
// 定制响应
class Response
{
public:Response():result_(0),exitCode_(0){}~Response(){}// 序列化void serialization(std::string *out){// exitCode_ result_std::string ec std::to_string(exitCode_);std::string res std::to_string(result_);*out ec;*out SPACE;*out res;}// 反序列化bool deserialization(std::string in){// nextiCode_ result_size_t pos in.find(SPACE);if(pos std::string::npos) return false;std::string ec in.substr(0, pos);std::string res in.substr(pos SPACE_LEN);exitCode_ atoi(ec.c_str());result_ atoi(res.c_str());return true;}public:// 退出状态0标识运算结果合法非0标识运算结果是非法的int exitCode_; // 运算结果int result_;
};输入参数读取
我们在客户端输入时是按照字符串的格式输入的所以为了能够使用定制协议需要进行反序列化即将输入的字符串数据按照协议中的结构化数据提取出来。
#define OPS -*/%bool makeReuquest(const std::string str, Request *req)
{// 1231 1*1 1/1char strtmp[1024];snprintf(strtmp, sizeof strtmp, %s, str.c_str());char *left strtok(strtmp, OPS);if (!left)return false;char *right strtok(nullptr, OPS);if (!right)return false;char mid str[strlen(left)];req-getX() atoi(left);req-getY() atoi(right);req-getOp() mid;return true;
}测试
IP127.0.0.1 --》测试 IP119.91.213.117 --》测试
2.3 使用第三方库
大佬也写了第三方库来支持序列化和反序列化在这里我使用的jsoncpp安装方式如下
sudo yum install -y jsoncpp-devel第三方库的使用较之我们自己实现无疑方便了很多不用自己造轮子。
#pragma once
#include iostream
#include string
#include assert.h
#include jsoncpp/json/json.h#include Log.hpp#define CRLF \r\n
#define CRLF_LEN strlen(CRLF)
#define SPACE
#define SPACE_LEN strlen(SPACE)
#define OPS -*/%// 定制请求
class Request
{
public:Request(){}~Request(){}int getX(){ return x_;}int getY(){ return y_;}char getOp(){ return op_;}// 序列化void serialization(std::string *out){//json// 1. Value对象万能对象// 2. json是基于KV// 3. json有两套操作方法// 4. 序列化的时候会将所有的数据内容转换成为字符串Json::Value root; //万能对象任意类型root[x] x_;root[y] y_;root[op] op_;Json::FastWriter fw; //Json::StyledWriter fw;*out fw.write(root); //序列化}// 反序列化bool deserialization(std::string in){Json::Value root;Json::Reader rd;rd.parse(in, root);x_ root[x].asInt();y_ root[y].asInt();op_ root[op].asInt();return true;}void debug(){std::cout x_ x_ std::endl;std::cout y_ y_ std::endl;std::cout op_ op_ std::endl;}
private:// 需要计算的数据int x_; int y_;// 需要计算的种类, - * / %char op_;
};// 定制响应
class Response
{
public:Response():result_(0),exitCode_(0){}~Response(){}// 序列化void serialization(std::string *out){Json::Value root;root[exitCode] exitCode_;root[result] result_;Json::FastWriter fw;*out fw.write(root); //序列化}// 反序列化bool deserialization(std::string in){Json::Value root;Json::Reader rd;rd.parse(in, root);exitCode_ root[exitCode].asInt();result_ root[result].asInt();return true;}public:// 退出状态0标识运算结果合法非0标识运算结果是非法的int exitCode_; // 运算结果int result_;
};
测试
使用Json::FastWriter fw; 使用Json::StyledWriter fw;