深圳做律师网站公司,seo是什么地方,北京网站优化开户,友情链接代码wordpress应用层协议 一、协议定制---以网络计算器为例
网络计算机功能---进行-*/^|的运算并返回结果
请求和响应的结构体如下
// Protocol.hpp
#pragma once
#include iostream
#include memory
class Request
{
public:Request(){}Request(int data_x, int da…应用层协议 一、协议定制---以网络计算器为例
网络计算机功能---进行-*/^|的运算并返回结果
请求和响应的结构体如下
// Protocol.hpp
#pragma once
#include iostream
#include memory
class Request
{
public:Request(){}Request(int data_x, int data_y, char op): _data_x(data_x), _data_y(data_y), _op(op){}private:int _data_x;int _data_y;char _op;
};
class Response
{
public:Response(){}Response(int result, int code): _result(result), _code(code){}private:int _result;int _code;
};// 建造类 --- 设计模式 --- 用来创建类对象
class Factory
{
public:std::shared_ptrRequest BuiltRequest(){std::shared_ptrRequest req std::make_sharedRequest();return req;}std::shared_ptrRequest BuiltRequest(int data_x, int data_y, char op){std::shared_ptrRequest req std::make_sharedRequest(data_x, data_y, op);return req;}std::shared_ptrResponse BuiltResponse(){std::shared_ptrResponse resp std::make_sharedResponse();return resp;}std::shared_ptrResponse BuiltResponse(int result, int code){std::shared_ptrResponse resp std::make_sharedResponse(result, code);return resp;}
};
将请求的数据按照怎样的方式序列化成为字符串呢
我们选择将数据转换成 len\nx op y\n
len是x op y的字符串长度len后面的\n用来将len和后面的有效数据分开”x op y“是要计算的表达式比如”5 6“中间加空格间隔开它后面的\n是为了调试打印可以不加
这里我们也可以直接选择将数据转化成 x op y\n用\n作为数据之间的间隔但是如果我们要传递的数据中就包含\n这样做就会出现问题但是加len\n就不会因为我们能保证len这个字符串中不可能出现\n更具有通用性
二、序列化和反序列化
所以序列化和反序列化本质就是字符串相关的操作具体的代码如下
//新增 给字符串数据加报头和去报头的函数
const std::string BlankSep ;
const std::string LineSep \n;// 将有效数据 - len\n有效数据\n
std::string Encode(const std::string message)
{return std::to_string(message.size()) LineSep message LineSep;
}// 将len\n有效数据\n - 有效数据这里要注意判断数据是否完整即是否有一个完整的请求
bool Decode(std::string package, std::string *message)
{auto pos package.find(LineSep);if (pos std::string::npos)return false;int len std::stoi(package.substr(0, pos));int total len pos 2 * LineSep.size();if (total package.size())return false;*message package.substr(pos LineSep.size(), len);package.erase(0, total);return true;
}class Request
{//...
public:// 新增 --- 序列化和反序列化两个函数// 将数据序列化为 x op y, 其中BlankSep是 void Serialize(std::string *out){*out std::to_string(_data_x) BlankSep _op BlankSep std::to_string(_data_y);}// 将x op y反序列化结构化字段, 其中BlankSep是 bool Deserialize(const std::string in){auto l in.find(BlankSep);if (l std::string::npos)return false;_data_x std::stoi(in.substr(0, l));auto r in.rfind(BlankSep);if (r std::string::npos)return false;_data_y std::stoi(in.substr(r BlankSep.size()));if (r - l - (int)BlankSep.size() ! 1)return false;_op in[l BlankSep.size()];return true;}
};class Response
{//...
public:// 新增 --- 序列化和反序列化两个函数// 将数据转序列化为result code, 其中BlankSep是 void Serialize(std::string *out){*out std::to_string(_result) BlankSep std::to_string(_code);}// 将result code反序列化为结构化字段, 其中BlankSep是 bool Deserialize(const std::string in){auto pos in.find(BlankSep);if (pos std::string::npos)return false;_result std::stoi(in.substr(0, pos));_code std::stoi(in.substr(pos BlankSep.size()));return true;}
};
当然这些字符串相关的序列化和反序列化操作过于繁琐我们可以不用手写可以直接用一些工具帮助我们完成这一过程如jsonprotobuf等。
用json举个例子(注意要下载json相关的第三方库)
class Request
{//...
public:void Serialize(std::string *out){Json::Value root;root[data_x] _data_x;root[data_y] _data_y;root[op] _op;Json::FastWriter writer;*out writer.write(root);}bool Deserialize(const std::string in){Json::Value root;Json::Reader reader;bool res reader.parse(in, root);if (res){_data_x root[data_x].asInt();_data_y root[data_y].asInt();_op root[op].asInt();}return res;}
};class Response
{//...
public:void Serialize(std::string *out){Json::Value root;root[result] _result;root[code] _code;Json::FastWriter writer;*out writer.write(root);}bool Deserialize(const std::string in){Json::Value root;Json::Reader reader;bool res reader.parse(in, root);if (res){_result root[result].asInt();_code root[code].asInt();}return res;}
}; Json序列化后的表达式如下 三、完整代码(包含对socket的封装)
// Calculate.hpp
#pragma once
#include iostream
#include Protocol.hpp
namespace Calcu
{enum{Success,DivZero,ModZero,Unknown};class Calculate{public:std::shared_ptrProtocol::Response cal(std::shared_ptrProtocol::Request req){auto resp factory.BuiltResponse();switch (req-GetOp()){case :resp-SetResult(req-GetX() req-GetY());break;case -:resp-SetResult(req-GetX() - req-GetY());break;case *:resp-SetResult(req-GetX() * req-GetY());break;case /:{if (req-GetY() 0){resp-SetCode(DivZero);}else{resp-SetResult(req-GetX() / req-GetY());}}break;case %:{if (req-GetY() 0){resp-SetCode(ModZero);}else{resp-SetResult(req-GetX() % req-GetY());}}break;case ^:resp-SetResult(req-GetX() ^ req-GetY());break;case |:resp-SetResult(req-GetX() | req-GetY());break;case :resp-SetResult(req-GetX() req-GetY());break;default:resp-SetCode(Unknown);break;}return resp;}private:Protocol::Factory factory;};
}//Protocol.hpp
#pragma once
#include iostream
#include memory
#include string
#include jsoncpp/json/json.h// #define SelfDefine 1namespace Protocol
{const std::string BlankSep ;const std::string LineSep \n;std::string Encode(const std::string message){return std::to_string(message.size()) LineSep message LineSep;}bool Decode(std::string package, std::string *message){auto pos package.find(LineSep);if (pos std::string::npos)return false;int len std::stoi(package.substr(0, pos));int total len pos 2 * LineSep.size();if (total package.size())return false;*message package.substr(pos LineSep.size(), len);package.erase(0, total);return true;}class Request{public:Request() : _data_x(0), _data_y(0), _op(0){}Request(int data_x, int data_y, char op): _data_x(data_x), _data_y(data_y), _op(op){}void Serialize(std::string *out){
#ifdef SelfDefine*out std::to_string(_data_x) BlankSep _op BlankSep std::to_string(_data_y);
#elseJson::Value root;root[data_x] _data_x;root[data_y] _data_y;root[op] _op;Json::FastWriter writer;*out writer.write(root);
#endif}bool Deserialize(const std::string in){
#ifdef SelfDefineauto l in.find(BlankSep);if (l std::string::npos)return false;_data_x std::stoi(in.substr(0, l));auto r in.rfind(BlankSep);if (r std::string::npos)return false;_data_y std::stoi(in.substr(r BlankSep.size()));if (r - l - (int)BlankSep.size() ! 1)return false;_op in[l BlankSep.size()];return true;
#elseJson::Value root;Json::Reader reader;bool res reader.parse(in, root);if (res){_data_x root[data_x].asInt();_data_y root[data_y].asInt();_op root[op].asInt();}return res;
#endif}void Inc(){_data_x;_data_y;}void DeBug(){std::cout data_x : _data_x std::endl;std::cout data_y : _data_y std::endl;std::cout op : _op std::endl;}int GetX() { return _data_x; }int GetY() { return _data_y; }char GetOp() { return _op; }private:// 序列化// len\nx op y\nint _data_x;int _data_y;char _op;};class Response{public:Response() : _result(0), _code(0){}Response(int result, int code): _result(result), _code(code){}void Serialize(std::string *out){
#ifdef SelfDefine*out std::to_string(_result) BlankSep std::to_string(_code);
#elseJson::Value root;root[result] _result;root[code] _code;Json::FastWriter writer;*out writer.write(root);
#endif}bool Deserialize(const std::string in){
#ifdef SelfDefineauto pos in.find(BlankSep);if (pos std::string::npos)return false;_result std::stoi(in.substr(0, pos));_code std::stoi(in.substr(pos BlankSep.size()));return true;
#elseJson::Value root;Json::Reader reader;bool res reader.parse(in, root);if (res){_result root[result].asInt();_code root[code].asInt();}return res;
#endif}void SetResult(int res){_result res;}void SetCode(int code){_code code;}int GetResult(){return _result;}int GetCode(){return _code;}private:int _result;int _code;};// 建造类 --- 设计模式class Factory{public:std::shared_ptrRequest BuiltRequest(){std::shared_ptrRequest req std::make_sharedRequest();return req;}std::shared_ptrRequest BuiltRequest(int data_x, int data_y, char op){std::shared_ptrRequest req std::make_sharedRequest(data_x, data_y, op);return req;}std::shared_ptrResponse BuiltResponse(){std::shared_ptrResponse resp std::make_sharedResponse();return resp;}std::shared_ptrResponse BuiltResponse(int result, int code){std::shared_ptrResponse resp std::make_sharedResponse(result, code);return resp;}};
}// 对sockfd进行封装
//对网络套接字进行封装
// Socket.hpp
#pragma once
#include iostream
#include sys/types.h
#include sys/socket.h
#include arpa/inet.h#include string.h
#include memory
#include string
#include unistd.hconst int defaultsockfd -1;
const int defaultbacklog 5;
#define CONV(addr) ((struct sockaddr *)addr)
namespace zxws
{enum{SockError 1,BindError,ListenError,AcceptError,ConnectError};// 模板方法 --- 设计模式的一种// 对网络套接字进行封装 --- 屏蔽内部建立连接的过程class Socket{public:virtual ~Socket() {}virtual void CreateSocket() 0;virtual void BindSocket(uint16_t port) 0;virtual void ListenSocketOrDie(int backlog) 0;virtual Socket *AcceptConnection(std::string *peerip, uint16_t *peerport) 0;virtual bool ConnectServer(const std::string peerip, uint16_t peerport) 0;virtual int GetSockfd() 0;virtual void SetSockFd(int sockfd) 0;virtual void CloseSockfd() 0;public:void BuildListenSocket(uint16_t port, int backlog){CreateSocket();BindSocket(port);ListenSocketOrDie(backlog);}bool BuildConnectSocket(const std::string peerip, uint16_t peerport){CreateSocket();return ConnectServer(peerip, peerport);}void BuildNormalSocket(int sockfd){SetSockFd(sockfd);}};class TcpSocket : public Socket{public:TcpSocket(int sockfd defaultsockfd) : _sockfd(sockfd){}virtual ~TcpSocket() {}virtual void CreateSocket() override{_sockfd socket(AF_INET, SOCK_STREAM, 0);if (_sockfd 0)exit(SockError);std::cout create success , sockfd : _sockfd std::endl;}virtual void BindSocket(uint16_t port) override{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;int n bind(_sockfd, CONV(local), sizeof(local));if (n 0)exit(BindError);std::cout bing success , sockfd: _sockfd std::endl;}virtual void ListenSocketOrDie(int backlog) override{int n listen(_sockfd, backlog);if (n 0)exit(ListenError);std::cout listen success , sockfd: _sockfd std::endl;}virtual Socket *AcceptConnection(std::string *peerip, uint16_t *peerport){struct sockaddr_in server;socklen_t len sizeof(server);int sockfd accept(_sockfd, CONV(server), len);if (sockfd 0)return nullptr;char buffer[INET_ADDRSTRLEN];inet_ntop(AF_INET, server.sin_addr, buffer, sizeof(buffer));*peerip buffer;*peerport ntohs(server.sin_port);return new TcpSocket(sockfd);}virtual bool ConnectServer(const std::string peerip, uint16_t peerport) override{struct sockaddr_in server;memset(server, 0, sizeof(server));server.sin_family AF_INET;server.sin_port htons(peerport);inet_pton(_sockfd, peerip.c_str(), server.sin_addr);int n connect(_sockfd, CONV(server), sizeof(server));return n 0;}virtual int GetSockfd(){return _sockfd;}virtual void SetSockFd(int sockfd){_sockfd sockfd;}void CloseSockfd(){if (_sockfd 0)close(_sockfd);}private:int _sockfd;};
}//TcpServer.hpp
#pragma once
#include Socket.hpp
#include pthread.h
#include functionalusing func_t std::functionstd::string(std::string,bool*);
class TcpServer;class ThreadData
{
public:ThreadData(TcpServer* tcp_this,zxws::Socket* sockp):_this(tcp_this),_sockp(sockp){}
public:TcpServer*_this;zxws::Socket* _sockp;
};class TcpServer
{
public:TcpServer(int port, func_t task): _port(port), _listensockfd(new zxws::TcpSocket()), _task(task){}static void *ThreadRoutine(void *args){pthread_detach(pthread_self());ThreadData *td static_castThreadData *(args);std::string inbuffer;while (1){bool ok true;// 1、接收if(!td-_sockp-Recv(inbuffer,1024))break;// 2、处理std::string send_str td-_this-_task(inbuffer,ok);if(ok){if(!send_str.empty()){// 3、发送td-_sockp-Send(send_str);}}else{break;}}//注意顺序td-_sockp-CloseSockfd();delete td-_sockp;delete td;return nullptr;}void Loop(){_listensockfd-BuildListenSocket(_port, defaultbacklog);std::string ip;uint16_t port;while (true){zxws::Socket *sockfd _listensockfd-AcceptConnection(ip, port);if (sockfd nullptr)continue;std::cout accept success , [ ip : port ] sockfd sockfd-GetSockfd() std::endl;// sockfd-CloseSockfd();ThreadData *td new ThreadData(this,sockfd);pthread_t tid;pthread_create(tid, nullptr, ThreadRoutine, td);}}~TcpServer(){delete _listensockfd;}private:int _port;zxws::Socket *_listensockfd;func_t _task;
};//TcpServer.cpp
#include Socket.hpp
#include TcpServer.hpp
#include Protocol.hpp
#include Calculate.hpp
using namespace Protocol;
using namespace zxws;
using namespace Calcu;
std::string Handler(std::string inbuffer, bool *error_code)
{*error_code true;std::unique_ptrFactory factory(new Factory);auto req factory-BuiltRequest();Calculate calculate;// 1、分析字节流std::string message;std::string ret;while (Decode(inbuffer, message)){// 2、反序列化if (!req-Deserialize(message)){*error_code false;return std::string();}// 3、业务逻辑auto resp calculate.cal(req);// 4、序列化std::string send_str;resp-Serialize(send_str);// 5、添加报头send_str Encode(send_str);ret send_str;}return ret;
}int main(int argc, char *argv[])
{if (argc ! 2){std::cout Usage : argv[0] port std::endl;return 0;}uint16_t port std::stoi(argv[1]);std::unique_ptrTcpServer svr(new TcpServer(port, Handler));svr-Loop();return 0;
}//TcpClient.cpp
#include Socket.hpp
#include Protocol.hpp
using namespace Protocol;
const std::string opers -/*%^|;
int main(int argc, char *argv[])
{if (argc ! 3){std::cout Usage : argv[0] ip port std::endl;return 0;}uint16_t port std::stoi(argv[2]);std::string ip argv[1];zxws::Socket *sock new zxws::TcpSocket();if (!sock-BuildConnectSocket(ip, port)){std::cout connect failed std::endl;return 0;}std::cout connect success std::endl;std::unique_ptrFactory factory(new Factory);srand(time(nullptr));std::string response;while (1){// 1、创建消息int x rand() % 100;int y rand() % 100;char op opers[rand() % opers.size()];usleep(1000);std::shared_ptrRequest req factory-BuiltRequest(x, y, op);req-DeBug();// 2、序列化std::string requires;req-Serialize(requires);std::coutrequiresstd::endl;// 3、添加报头std::string res Encode(requires);// 4、发送sock-Send(res);// 5、接收sock-Recv(response, 1024);// 6、分析字节流std::string message;if (!Decode(response, message))break;// 7、反序列化auto resp factory-BuiltResponse();if (!resp-Deserialize(message))break;// 打印std::cout x op y resp-GetResult() [ resp-GetCode() ] std::endl;}sock-CloseSockfd();return 0;
}
四、总结和扩展 当然这只是一个协议我们以后如果想要增加功能可以再去定义协议只要在序列化数据的头部加上该请求想要访问哪个服务的即可其他操作步骤和我们制定网络计算器一样。