佛山本地的网站设计公司,关于我的大学的网站建设模板,镇江搜索优化技巧,海门市城乡建设局网站目录 一、序列化与反序列化概念
二、自定协议实现一个加法网络计算器
#xff08;一#xff09;TCP如何保证接收方的接收到数据是完整性呢#xff1f;
#xff08;二#xff09;自定义协议
#xff08;三#xff09;自定义协议的实现
1、基础类
2、序列化与反序列…目录 一、序列化与反序列化概念
二、自定协议实现一个加法网络计算器
一TCP如何保证接收方的接收到数据是完整性呢
二自定义协议
三自定义协议的实现
1、基础类
2、序列化与反序列化
3、报头的增删
4、从缓冲区内提取完整报文
5、自定协议在服务器与客户端的实现
三、使用Json进行序列化和反序列化
一概念
二Json的安装
三针对序列化和反序列化的改造 一、序列化与反序列化概念 在之前的文章中我们调用网络接口进行数据传输时都是使用字符串作为数据直接进行传输的但是在一些场景中单个字符串是不能满足需要的可能需要一个结构体或是数据结构那么如何将结构体或数据结构进行网络传输呢 序列化将数据结构或对象转换为字节流以便它们可以存储到磁盘上、通过网络传输或在不同的程序间共享 反序列化存储的字节流或文本数据重新转换为原来的数据结构或对象。这个过程用于恢复存储的或传输的数据以便程序能再次使用它。 当我们需要发送一个结构体时我们可以先将其进行序列化转换为字节流再进行网络传输而接收方可以通过将其反序列化从而恢复传输的数据以此达到数据传输的目的。 二、自定协议实现一个加法网络计算器
一TCP如何保证接收方的接收到数据是完整性呢 UDP是面向数据报而TCP是面向字节流的。 在UDP协议传输数据时由于数据的发送和接收都是按数据报的格式进行的每个数据报是独立传输的。虽然UDP协议本身不保证数据的完整性和可靠性但在网络传输没有出现丢包或错误的情况下数据是完整的。 但在使用TCP协议传输数据时由于数据的发送和接收都是按字节流的形式进行的发送和接收到的数据不一定是完整的假如TCP的服务端读取速度小于TCP客户端的发送速度那么在缓冲区一定堆积了大量的报文那么如何从缓冲区内提取到一条完整的报文数据呢 其实我们调用接口进行网络数据传输本质实际是拷贝。发送方发送数据接收方接收数据本质实际是发送方缓冲区内容拷贝给接收方缓冲区。 对于上述的问题如果发送方发送数据的速度过快导致接收方缓冲区内堆积了大量的报文那么如何从大量数据中提取出一个完整的报文呢 实际上可以定制协议以下是协议设计方式
定长规定每个报文的固定长度特殊符合在报文之间加上特殊符合用于分割报文自描述方式数据本身描述其格式、大小等
二自定义协议 本文采用自描述方式设计自定协议 首先本文是针对加法计算器而做的协议而这个协议不仅要包括数据的序列化和反序列化还要有增添减少报头分割符的接口。除此之外因为使用面向字节流进行输出还要保证如何从缓冲区内提取一个完整的结构。
三自定义协议的实现
1、基础类
class Request
{
public:int _x; //左操作数int _y; //右操作数char _op; //操作符
};
class Response
{
public:int _exitcode; //退出码int _result; //计算结果
};
2、序列化与反序列化
class Request
{
public:Request() {}Request(int x, int y, char op) : _x(x), _y(y), _op(op) {}//序列化bool serialize(string out){out.clear();out to_string(_x);out SEP;out _op;out SEP;out to_string(_y);return true;}//反序列化bool deserialize(const string in){auto left in.find(SEP);auto right in.rfind(SEP);if (left string::npos || right string::npos || left right)return false;if (right - left - SEP_LEN ! 1)return false;string x_string in.substr(0, left);string y_string in.substr(right SEP_LEN);if (x_string.empty() || y_string.empty())return false;_x stoi(x_string);_y stoi(y_string);_op in[left SEP_LEN];return true;}
public:int _x;int _y;char _op;
};
class Response
{
public:Response() {}Response(int exitcode, int result) : _exitcode(exitcode), _result(result) {}//序列化bool serialize(string out){out.clear();out to_string(_exitcode);out SEP;out to_string(_result);return true;}//反序列化bool deserialize(const string in){auto index in.find(SEP);if (index string::npos)return false;string code in.substr(0, index);string result in.substr(index SEP_LEN);if (code.empty() || result.empty())return false;_exitcode stoi(code);_result stoi(result);return true;}
public:int _exitcode;int _result;
};
3、报头的增删
#define SEP
#define SEP_LEN strlen(SEP)
#define SEP_LINE \r\n
#define SEP_LINE_LEN strlen(SEP_LINE)
//text_len/r/text/r/n
//增添报头
bool enLength(string text)
{if (text.empty())return false;int len text.size();string len_string to_string(len);if (len_string.empty())return false;string ret;ret len_string;ret SEP_LINE;ret text;ret SEP_LINE;text ret;return true;
}
//删除报头
bool deLength(const string package, string text)
{auto index package.find(SEP_LINE);if (index string::npos)return false;string len_string package.substr(0, index);int len stoi(len_string);text.clear();text package.substr(index SEP_LINE_LEN, len);return true;
}
4、从缓冲区内提取完整报文
bool recvPackage(const int fd, string inbuffer, string text)
{while (true){char buffer[1024];ssize_t n recv(fd, buffer, sizeof(buffer) - 1, 0);if (n 0){buffer[n] 0;inbuffer buffer;auto index inbuffer.find(SEP_LINE);string len_string inbuffer.substr(0, index);int len stoi(len_string);int total len SEP_LINE_LEN * 2 len_string.size();if (inbuffer.size() total){cout 正在等待后续消息 endl;continue;}text inbuffer.substr(0, total);inbuffer.erase(0, total);break;}elsereturn false;}return true;
}
5、自定协议在服务器与客户端的实现
//服务端
void handlerEntery(const int fd, func_t func){string inbuffer;while (1){// 收到ReqRequest req;string req_str, req_text;if (recvPackage(fd, inbuffer, req_str) false)return;// 去报头cout 接收数据 req_str endl;bool ret deLength(req_str, req_text);if (ret false)logMessage(ERROR, Server : deLength fail);cout 接收数据正文 req_text endl;// 反序列化ret req.deserialize(req_text);if (ret false)logMessage(ERROR, Server : deserialize fail);// 计算Response resp;func(req, resp);// 序列化string resp_text;ret resp.serialize(resp_text);if (ret false)logMessage(ERROR, Server : deserialize fail);cout 发送数据 resp_text endl;// 加报头ret enLength(resp_text);if (ret false)logMessage(ERROR, Server : enLength fail);cout 发送数据正文 resp_text endl;// 发送Respsend(fd, resp_text.c_str(), resp_text.size(), 0);}}
//客户端
void run(){struct sockaddr_in addr;bzero(addr, 0);addr.sin_family AF_INET;addr.sin_addr.s_addr inet_addr(_Sip.c_str());addr.sin_port htons(_Sport);if (connect(_fd, (struct sockaddr *)addr, sizeof(addr)) -1){cerr strerror(errno) endl;exit(2);}else{string inbuffer;while (true){// 读取数据string line;cout please input#\n;getline(cin, line);Request req parseLine(line);// 序列化string req_text;bool ret req.serialize(req_text);if (ret false)logMessage(ERROR, Client : serialize fail);// 加报头ret enLength(req_text);if (ret false)logMessage(ERROR, Client : enLength fail);// 发数据send(_fd, req_text.c_str(), req_text.size(), 0);// 接收数据string resp_str, resp_text;if (!recvPackage(_fd, inbuffer, resp_str))continue;// 去报头ret deLength(resp_str, resp_text);if (ret false)logMessage(ERROR, Client : deLength fail);// 反序列化Response resp;ret resp.deserialize(resp_text);if (ret false)logMessage(ERROR, Client : deserialize fail);cout exitcode : resp._exitcode \tresulet : resp._result endl;}}}
三、使用Json进行序列化和反序列化
一概念 从上文可以得知手动进行序列化和反序列化非常繁杂。实际上我们可以使用现成的方案。 Json是一种轻量级的数据交换格式广泛用于网络应用中尤其是在客户端与服务器之间的数据交换。它易于阅读和编写同时也便于机器解析和生成。
二Json的安装 首先登录云服务器后输入指令
sudo yum install -y jsonapp-devel 使用Json除了需要引入头文件以外还需要在编译中加入 -ljsoncpp 选项。
#include jsoncpp/json/json.h
g -g -o Server Server.cpp -stdc11 -ljsoncpp
三针对序列化和反序列化的改造
class Request
{
public:Request() {}Request(int x, int y, char op) : _x(x), _y(y), _op(op) {}//序列化bool serialize(string out){Json::Value root;root[left] _x;root[right] _y;root[op] _op;Json::FastWriter writer;out writer.write(root); return true;}//反序列化bool deserialize(const string in){Json::Value root;Json::Reader reader;reader.parse(in, root);_x root[left].asInt();_y root[right].asInt();_op root[op].asInt();return true;}
public:int _x;int _y;char _op;
};
class Response
{
public:Response() {}Response(int exitcode, int result) : _exitcode(exitcode), _result(result) {}//序列化bool serialize(string out){Json::Value root;root[exitcode] _exitcode;root[result] _result;Json::FastWriter writer;out writer.write(root);return true;}//反序列化bool deserialize(const string in){Json::Value root;Json::Reader reader;reader.parse(in, root);_exitcode root[exitcode].asInt();_result root[result].asInt();return true;}public:int _exitcode;int _result;
};