协议
什么是“协议”
- 在计算机科学中,协议(Protocol)是对数据格式和计算机之间交换数据时必须遵守的规则的正式描述。例如,网络中的计算机要能够互相顺利通信,就必须遵守相同的协议,如Ethernet、NetBEUI、IPX/SPX以及TCP/IP协议
为什么要定制协议
- 为了使数据在网络上能够从源到达目的,网络通信的参与方必须遵循相同的规则,我们将这套规则称为协议(protocol),而协议最终都需要通过计算机语言的方式表示出来。只有通信计算机双方都遵守相同的协议,计算机之间才能互相通信交流。
如何定制协议
定制结构体:用该结构体来表示需要交互的信息
序列化:将对象的状态信息转换为可以存储或传输的形式(字节序列)
反序列化:把字节序列恢复为结构体对象
网络版本计算器
接下来我们通过自定义协议来实现一个网络版本的计算器,来真正理解一下协议
源码连接:网络版本计算器
协议定制
Request
_data_x
:左操作数_data_y
:右操作数_op
:运算符
response
_result
:计算结果_code
:状态码
class Request
{
public:
Request(int x, int y, char op)
: _data_x(x), _data_y(y), _op(op)
{}
Request()
: _data_x(0), _data_y(0), _op(0)
{}
private:
int _data_x;
int _data_y;
char _op;
};
class Response
{
public:
Response()
: _result(0), _code(0)
{}
Response(int result, int code)
: _result(result), _code(code)
{}
private:
int _result;
int _code;
};
enum
{
Success = 0, //成功
DivZero, //除零错误
ModZero, //模零错误
UnknownOp //未知错误
};
客户端
- 客户端在给服务器发送请求时,会经过以下几个步骤:
- 将请求序列化,即
Serialize
- 添加自描述报头,将响应构建成为一个完整报文,即
Encode
- 发送请求
- 得到响应报文
- 对响应报文解析,即
Decode
- 反序列化,即
Deserialize
,得到结果_result
以及状态码_status
Request类
我们封装另一个Request类,类内成员就是左右操作数以及操作符,类内提供了对请求进行序列化的Serialize
函数以及对请求进行反序列化的函数Deserialize
class Request
{
public:
Request(int x, int y, char op)
: _data_x(x), _data_y(y), _op(op)
{
}
Request()
: _data_x(0), _data_y(0), _op(0)
{
}
bool Serialize(std::string *out) //"x op y"
{
#ifdef SelfDefine
*out = std::to_string(_data_x) + ProtSep + _op + ProtSep + std::to_string(_data_y);
return true;
#else
Json::Value root;
root["data_x"] = _data_x;
root["data_y"] = _data_y;
root["oper"] = _op;
Json::FastWriter writer;
*out = writer.write(root);
return true;
#endif
}
bool Deserialize(std::string &in)
{
#ifdef SelfDefine
auto left = in.find(ProtSep);
if (left == std::string::npos)
return false;
auto right = in.rfind(ProtSep);
if (right == std::string::npos)
return false;
_data_x = std::stoi(in.substr(0, left));
_data_y = std::stoi(in.substr(right + ProtSep.size()));
std::string oper = in.substr(left + ProtSep.size(), right - (left + ProtSep.size()));
if (oper.size() != 1)
return false;
_op = oper[0];
return true;
#else
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["oper"].asInt();
}
return true;
#endif
}
void Debug()
{
std::cout << "_data_x: " << _data_x << std::endl;
std::cout << "_data_y: " << _data_y << std::endl;
std::cout << "_op: " << _op << std::endl;
}
void Inc()
{
_data_x++;
_data_y++;
}
int GetX()
{
return _data_x;
}
int GetY()
{
return _data_y;
}
char GetOp()
{
return _op;
}
private:
int _data_x;
int _data_y;
char _op;
};
#include <iostream>
#include <unistd.h>
#include <string>
#include <memory>
#include <ctime>
#include <stdlib.h>
#include "Socket.hpp"
#include "Protocol.hpp"
using namespace ProtocolNS;
//./tcpclinet serverip serverport
int main(int argc, char *argv[])
{
if (argc != 3)
{
std::cout << "Usage: \n\t" << argv[0] << " serverip serverport\n"
<< std::endl;
return 0;
}
std::string serverip = argv[1];
uint16_t serverport = std::stoi(argv[2]);
NetWork::Socket *conn = new NetWork::TcpSocket();
if (!conn->BuildConnectionSocketMethod(serverip, serverport))
{
std::cerr << "connect " << serverip << ":" << serverport << " failed" << std::endl;
}
std::cerr << "connect " << serverip << ":" << serverport << " success" << std::endl;
std::unique_ptr<Factory> factory = std::make_unique<Factory>();
srand(time(nullptr) ^ getpid());
const std::string opers = "+-*/%^&=";
while (true)
{
//1.构建一个请求
int x = rand()%100;
usleep(1234);
int y = rand()%100;
char oper = opers[rand() % opers.size()];
std::shared_ptr<Request> req = factory->BuildRequest(x,y,oper);
//2.对请求进行序列化
std::string requeststr;
req->Serialize(&requeststr);
std::string testreq = requeststr;
testreq += " = ";
//3.添加自描述报头
requeststr = Encode(requeststr);
//4.发送请求
conn->Send(requeststr);
std::string responsestr;
while(true)
{
//5.读取响应
conn->Recv(&responsestr,1024);
//6.对报文进行解析
std::string response;
if(!Decode(responsestr,&response))
continue;
//7.反序列化
auto resp = factory->BuildResponse();
resp->Deserialize(response);
std::cout << testreq << resp->Getresult() << "[" << resp->Getcode() << "]" << std::endl;
break;
}
sleep(1);
}
conn->CloseFd();
return 0;
}
服务器
- 服务器在收到客户端发来的请求后会经过以下几个步骤:
- 对收到的请求报文进行解析,即
Decode
- 反序列化,即
Deserialize
,得到操作数和运算符 - 业务处理(计算)
- 序列化response,即
Serialize
- 构建响应报文,即
Encode
- 发送给客户端
Response类
我们封装另一个Response
类,类内成员就是结果以及状态码,类内提供了对响应进行序列化的Serialize
函数以及对响应进行反序列化的函数Deserialize
class Response
{
public:
Response()
: _result(0), _code(0)
{
}
Response(int result, int code)
: _result(result), _code(code)
{
}
bool Serialize(std::string *out) //"_result _code"
{
#ifdef SelfDefine
*out = std::to_string(_result) + ProtSep + std::to_string(_code);
return true;
#else
Json::Value root;
root["result"] = _result;
root["code"] = _code;
Json::FastWriter writer;
*out = writer.write(root);
return true;
#endif
}
bool Deserialize(std::string &in)
{
#ifdef SelfDefine
auto pos = in.find(ProtSep);
if (pos == std::string::npos)
return false;
_result = std::stoi(in.substr(0, pos));
_code = std::stoi(in.substr(pos + ProtSep.size()));
return true;
#else
Json::Value root;
Json::Reader reader;
bool res = reader.parse(in,root);
if(res)
{
_result = root["result"].asInt();
_code = root["code"].asInt();
}
return true;
#endif
}
void SetResult(int result)
{
_result = result;
}
void SetCode(int code)
{
_code = code;
}
int Getresult()
{
return _result;
}
int Getcode()
{
return _code;
}
private:
int _result;
int _code;
};
#pragma once
#include "Socket.hpp"
#include <iostream>
#include <pthread.h>
#include <functional>
using func_t = std::function<std::string(std::string &, bool *)>;
class TcpServer;
class ThreadData
{
public:
ThreadData(TcpServer *tcp_this, NetWork::Socket *sockp)
: _this(tcp_this), _sockp(sockp)
{
}
public:
TcpServer *_this;
NetWork::Socket *_sockp;
};
class TcpServer
{
public:
TcpServer(uint16_t port, func_t handler_request)
: _port(port), _listensocket(new NetWork::TcpSocket()), _handler_request(handler_request)
{
_listensocket->BuildListenSocketMethod(_port, NetWork::backlog);
}
static void *ThreadRun(void *args)
{
pthread_detach(pthread_self());
ThreadData *td = static_cast<ThreadData *>(args);
std::string inbufferstream;
while (true)
{
bool ok = true;
// 1.读取报文
if (!td->_sockp->Recv(&inbufferstream, 1024))
break;
//2.报文处理
std::string send_string = td->_this->_handler_request(inbufferstream, &ok);
//3.发送数据
if(ok)
{
if(!send_string.empty())
{
td->_sockp->Send(send_string);
}
}
else
{
break;
}
}
td->_sockp->CloseFd();
delete td->_sockp;
delete td;
return nullptr;
}
void Loop()
{
while (true)
{
std::string peerip;
uint16_t peerport;
NetWork::Socket *newsock = _listensocket->AcceptConnection(&peerip, &peerport);
if (newsock == nullptr)
continue;
std::cout << "获取一个新连接,sockfd: " << newsock->GetSocket() << "客户信息:" << peerip << ":" << peerport << std::endl;
pthread_t tid;
ThreadData *td = new ThreadData(this, newsock);
pthread_create(&tid, nullptr, ThreadRun, td);
}
}
~TcpServer()
{
delete _listensocket;
}
private:
uint16_t _port;
NetWork::Socket *_listensocket;
public:
func_t _handler_request;
};
#include "Socket.hpp"
#include "TcpServer.hpp"
#include "Protocol.hpp"
#include "Calculate.hpp"
#include <iostream>
#include <string>
#include <memory>
using namespace NetWork;
using namespace ProtocolNS;
using namespace CalculateNs;
std::string HandlerRequest(std::string &inbufferstream, bool *error_code)
{
*error_code = true;
Calculate calculate;
std::unique_ptr<Factory> factory = std::make_unique<Factory>();
auto req = factory->BuildRequest();
// 2.查看报文是否完整
std::string message;
std::string total_resp_messgae;
while (Decode(inbufferstream, &message))
{
// 3.报文一定完整,反序列化req
if (!req->Deserialize(message))
{
*error_code = false;
return std::string();
}
// 4.业务处理
auto resp = calculate.Cal(req);
// 5.序列化response
std::string send_string;
resp->Serialize(&send_string);
// 6.构建字符串级别的响应报文
send_string = Encode(send_string);
// 7.发送
total_resp_messgae += send_string;
}
return total_resp_messgae;
}
//./tcpserver 8888
int main(int argc, char *argv[])
{
if (argc != 2)
{
std::cout << "Usage: \n\t" << argv[0] << " local_port\n"
<< std::endl;
return 0;
}
uint16_t port = std::stoi(argv[1]);
std::unique_ptr<TcpServer> tsvr(new TcpServer(port, HandlerRequest));
tsvr->Loop();
return 0;
}
样例演示
服务器启动后会进入死循环,不间断为客户端提供服务,客户端会随机生成操作数以及运算符,将请求发送给服务器后,服务器会将响应发送给客户端,客户端便会把结果打印输出在屏幕上