如何使用boost::asio实现嵌套协议
How to implement nested protocols with boost::asio?
我正在尝试编写一个处理协议a而不是协议B的服务器。
协议A是HTTP或RTSP,协议B是一个简单的二进制数据包序列:
[packet length][...encrypted packet data...]
所以我想用这样的东西:
boost::asio::async_read_until(socket, inputBuffer, "rnrn", read_handler);
但是,使用一些连接到协议B处理程序的伪套接字来代替socket
。
我有一些想法:
忘记
async_read
、async_read_until
等,为A和B编写两个状态机。混合方法:
async_read_*
用于协议B,状态机用于A.制作内部代理服务器。
我不喜欢(1)和(2),因为
很难将A与B解耦(我希望能够禁用协议B)。
丑陋。
(3) 只是看起来很难看:-)
所以问题是:我该如何实现这一点?
我过去做过类似于您的答案(2)的事情——使用async_read调用首先读取标头,然后使用另一个async_read来读取长度,并将剩余内容转发到手写状态机。但我不一定建议你这样做-因此,你可能会为协议B获得零拷贝IO,但当你知道后面总是有数据时,进行读取4-8字节标头的IO调用是非常浪费的。问题是,你对两层的网络抽象会有所不同-所以你提到的解耦问题确实存在。
使用固定长度的缓冲区,只调用async_read,然后用2个嵌套的状态机处理数据(就像您在回答(1)中基本上提出的那样)效果很好。每个的状态机都会简单地推送一些新接收的数据(直接从套接字或从较低的状态机)并进行处理。这意味着A在这里不会耦合到B,因为如果输入/输出数据格式匹配,您可以直接将数据从asio推送到A状态机。
与此类似的是Netty和Facebook Wangle库中使用的模式,在这些库中,处理程序可以从管道中的较低处理程序获取推送的数据,根据该输入执行操作,并将解码后的数据输出到下一个处理程序。这些处理程序可以是状态机,但根据协议的复杂性,不一定必须是状态机。您可以从中获得一些灵感,例如查看一些Wangle文档:https://github.com/facebook/wangle/blob/master/tutorial.md
如果你不想把数据从一个协议处理程序推送到另一个协议处理器,而是主动读取数据(很可能是以异步方式),你也可以自己设计一些接口(比如实现async_read(…)方法的ByteReader或允许读取完整消息而不是字节的PacketReader),通过代码实现它们(ByteReader也通过asio实现),并在更高级别上使用它们。因此,您将从数据处理的推送方法转向拉取方法,这有一些优点和缺点。
我不会讨论boost::asio,因为这似乎更像是一种设计模式,而不是一种网络模式。我会使用状态模式。这样你就可以随时更改协议。
class net_protocol {
protected:
socket sock;
public:
net_protocol(socket _sock) : sock(_sock) {}
virtual net_protocol* read(Result& r) = 0;
};
class http_protocol : public net_protocol {
public:
http_protocol(socket _sock) : net_protocol(_sock) {}
net_protocol* read(Result& r) {
boost::asio::async_read_until(socket, inputBuffer, "rnrn", read_handler);
// set result, or have read_handler set it
return this;
}
};
class binary_protocol : public net_protocol {
public:
binary_protocol(socket _sock) : net_protocol(_sock) {}
net_protocol* read(Result& r) {
// read 4 bytes as int size and then size bytes in a buffer. using boost::asio::async_read
// set result, or have read_handler set it
// change strategy example
//if (change_strategy)
// return new http_strategy(sock);
return this;
}
};
你可以用初始化启动协议
std::unique_ptr<net_protocol> proto(new http_protocol(sock));
然后你会阅读:
//Result result;
proto.reset(proto->read(result));
EDIT:if()返回的新策略实际上是一个状态机
如果您关心那些异步读取,因此无法决定哪些返回策略,请让策略类在其readhandler 中调用notify方法
class caller {
std::unique_ptr<net_protocol> protocol;
boost::mutex io_mutex;
public:
void notify_new_strategy(const net_protocol* p) {
boost::unique_lock<boost::mutex> scoped_lock(mutex);
protocol.reset(p);
}
void notify_new_result(const Result r) { ... }
};
如果您不需要动态更改使用的协议,则不需要State,因此read()
将返回Result
(或者,如果是async,则为void并调用caller::notify_new_result(const Result)
)。尽管如此,您仍然可以使用相同的方法(2个具体类和一个虚拟类),它可能非常接近策略模式
- 嵌套在类中时无法设置成员数据
- 无法访问嵌套类.类的使用无效
- 我正在使用嵌套的while循环来解析具有多行的文本文件,但由于某种原因,它只通过第一行,我不知道为什么
- 如何在C++中初始化嵌套类中的2个memeber
- 如何声明特征矩阵,然后通过嵌套循环初始化它
- 在C++中搜索嵌套多映射值
- 在C++中将矢量转换为嵌套地图
- C++嵌套if语句,基本货币交换
- 在nlohmann json中,如何将嵌套对象的数组转换为嵌套结构的向量
- 嵌套的匿名命名空间
- 了解嵌套循环打印星号图案
- 如何使用boost::具有嵌套结构和最小代码更改的序列化
- 嵌套for循环C++的问题(初学者)
- 从嵌套在std::映射中的std::列表中删除元素的最佳方式
- 用C#中的并集模拟C++嵌套结构
- 部分专业化和嵌套模板
- 嵌套While循环不起作用(C++问题)
- 协议缓冲区-读取所有消息中通用的标头(嵌套消息)
- 如何使用boost::asio实现嵌套协议
- 如何在协议缓冲区中设置嵌套消息的字段