-
Notifications
You must be signed in to change notification settings - Fork 0
/
byheadprotocol.h
155 lines (137 loc) · 5.28 KB
/
byheadprotocol.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#pragma once
#include "base/bytebuffer.hpp"
#include "base/endian.hpp"
#include "iprotocolbuilder.h"
#include "iprotocolparser.h"
namespace bcf
{
/**
* @brief 对于任何被bcf使用的自定义协议,要求【协议类型】和【会话唯一序列号】必须存在,seq后面的内容可以自定义
* @details
* \a【协议类型 type】: 供协议解析器来使用,因为支持多个解析器,所以需要根据此字段探测能否被其中的一个解析器所解析.
* \a【会话唯一序列号 seq】: 因为收到的是异步消息,所以bcf需要根据这个seq判断转发给哪个callback.
* \a【业务ID cmd】: 只是"指定头部长度协议"所需要的业务识别号,代表业务类型,bcf不关心,但是业务上应该关心是数据具体的哪个业务,比如是请求数据?还是删除数据?
* @see
* \a|*****************head*******************|*****body*****|
* \a| type | seq | cmd | length | XXX |
* \a| 1byte | 4byte | 4byte | 4bytes | length bytes |
* \a|****************************************|**************|
**/
class ByHeadProtocolModel : public AbstractProtocolModel
{
public:
uint32_t cmd = 0;
uint32_t length = 0;
constexpr static uint16_t body_offset = sizeof(type) + sizeof(seq) + sizeof(cmd) + sizeof(length);
void setBody(const std::string& body)
{
m_body = body;
length = m_body.length();
}
const std::string& body()
{
return m_body;
};
virtual PackMode protocolType() override
{
return PackMode::UNPACK_BY_LENGTH_FIELD;
};
//e.g. json or std::string
private:
std::string m_body;
};
class ByHeadProtocolBuilder : public IProtocolBuilder
{
public:
ByHeadProtocolBuilder(PackEndian endian = PackEndian::USE_BIG_ENDIAN): IProtocolBuilder(
endian) {};
virtual PackMode getType()const override
{
return PackMode::UNPACK_BY_LENGTH_FIELD;
};
virtual std::shared_ptr<bb::ByteBuffer> build(const std::shared_ptr<AbstractProtocolModel>&
_model)
override
{
std::shared_ptr<ByHeadProtocolModel> model =
std::dynamic_pointer_cast<ByHeadProtocolModel>(_model);
if (nullptr == model) {
return nullptr;
}
uint8_t type = getType();
uint32_t bigSeq = model->seq;
uint32_t bigCmd = model->cmd;
uint32_t bigLen = model->length;
if (m_endian == PackEndian::USE_BIG_ENDIAN) {
bigSeq = htobe32(model->seq);
bigCmd = htobe32(model->cmd);
bigLen = htobe32(model->length);
} else if (m_endian == PackEndian::USE_LITTEL_ENDIAN) {
bigSeq = htole32(model->seq);
bigCmd = htole32(model->cmd);
bigLen = htole32(model->length);
}
auto ptr = std::make_shared<bb::ByteBuffer>(model->body().length() + model->body_offset);
ptr->put(type);
ptr->putInt(bigSeq);
ptr->putInt(bigCmd);
ptr->putInt(bigLen);
ptr->putBytes((uint8_t*)model->body().c_str(), model->body().length());
return ptr;
};
};
class ByHeadProtocolParser : public IProtocolParser
{
public:
ByHeadProtocolParser(PackEndian endian = PackEndian::USE_BIG_ENDIAN): IProtocolParser(
endian) {};
virtual PackMode getType()const override
{
return PackMode::UNPACK_BY_LENGTH_FIELD;
};
/**
* @brief 协议解码器实现纯虚的parse函数,回调的目的是因为假如出现粘包、拆包现象,可以递归callback,每callback一次则代表完整的一帧,剩余的数据被缓存在协议解码器内部
*/
virtual void parse(const
std::function<void(ParserState state, std::shared_ptr<AbstractProtocolModel> model)>& callback)
override
{
int totalLength = m_buffer->size();
if (totalLength < ByHeadProtocolModel::body_offset) {
std::cout << "totalLength:" << totalLength << "<" << ByHeadProtocolModel::body_offset <<
std::endl;
return;
}
uint8_t type = m_buffer->getChar();
uint32_t seq = m_buffer->getInt();
uint16_t cmd = m_buffer->getInt();
uint32_t bodylength = m_buffer->getInt();
if (m_endian == PackEndian::USE_BIG_ENDIAN) {
seq = be32toh(seq);
cmd = be32toh(cmd);
bodylength = be32toh(bodylength);
} else if (m_endian == PackEndian::USE_LITTEL_ENDIAN) {
seq = le32toh(seq);
cmd = le32toh(cmd);
bodylength = le32toh(bodylength);
}
const int reqTotalLength = ByHeadProtocolModel::body_offset + bodylength;
if (totalLength < reqTotalLength) {
std::cout << "totalLength < " << reqTotalLength;
return;
}
bb::ByteBuffer body = m_buffer->mid(bodylength);
std::shared_ptr<ByHeadProtocolModel> model = std::make_shared<ByHeadProtocolModel>();
model->type = type;
model->seq = seq;
model->cmd = cmd;
model->setBody(std::string(body.begin(), body.end()));
callback(ParserState::OK, model);
if (0 == m_buffer->bytesRemaining()) {
m_buffer->clear();
return;
}
parse(callback);
};
};
}