本篇其实是前篇【Filezilla】 dispatch函数重载的例子-CSDN博客的一个补充,其中涉及到【FileZilla】事件调用机制代码解析-CSDN博客中的事件分发机制时钩子函数的参数传递怎么实现的。跟【FileZilla】sftp协议的数据传输上传和下载-CSDN博客同样,用事件是 CSftpEvent 即 send_event<T> () 或者说pending_events.emplace_back(handler, evt, deletable ) 调用时的T和evt,开始。
我们的问题是事件处理函数 CSftpControlSocket::OnSftpEvent(sftp_message const& message) 中的是怎么得到的消息message的?
// In event_loop.hpp
typedef std::deque<std::tuple<event_handler*, event_base*, bool>> Events;
// Events::value_type ev{};
// In event.h
typedef fz::simple_event<sftp_event_type, sftp_message> CSftpEvent;
// In sftpcontrolsocket.cpp
void CSftpControlSocket::operator()(fz::event_base const& ev)
{
if (fz::dispatch<fz::process_event, CSftpEvent, CSftpListEvent, SftpRateAvailableEvent>(ev, this,
&CSftpControlSocket::OnProcessEvent,
&CSftpControlSocket::OnSftpEvent,
&CSftpControlSocket::OnSftpListEvent,
&CSftpControlSocket::OnQuotaRequest)) {
return;
}
CControlSocket::operator()(ev);
}
// In sftpcontrolsocket.cpp
void CSftpControlSocket::OnSftpEvent(sftp_message const& message)
{
......
switch (message.type)
{
......
case sftpEvent::Recv:......break;
case sftpEvent::Send:......break;
case sftpEvent::Transfer:......break;
......
case sftpEvent::io_nextbuf:......break;
case sftpEvent::io_open:......break;
case sftpEvent::io_size:......break;
case sftpEvent::io_finalize:......break;
default:......
}
......
}
类似【FileZilla】事件调用机制代码解析-CSDN博客,在SftpInputParser::OnData()函数中 event_ = std::make_unique<CSftpEvent>() 随后调用事件入队列 owner_.send_event(event_.release()) ,以下式子逐步等价:
- owner_.send_event(event_.release())
- event_loop.send_event(this, event_.release(), true),this 指针指向子类CSftpControlSocket的实例
- 实际调用:pending_events_.emplace_back(handler,evt,deletable),handler就是this指针、evt就是CSftpEvent,event_.release()就是从智能指针中释放裸指针,交出所有权
其中owner_是CSftpControlSocket类型,CSftpControlSocket继承自 CControlSocket ,它继承自event_handler,公有父类实现了 send_event() 方法。于是,当调用entry()现成调process_event()时,语句 (*std::get<0>(ev))(*std::get<1>(ev)) 时,相当于以下式子逐步“等价”:
- (*std::get<0>(ev))(*std::get<1>(ev))
- (*handler)(evt)
- handler->operator()(evt)
- CSftpControlSocket的实例->operator()( CSftpEvent * ev ),operator实际接收了event_base类型
- CSftpControlSocket::operator()(fz::event_base const& ev)
于是从【FileZilla】sftp协议的数据传输上传和下载-CSDN博客中,我们知道事件分发后会调用CSftpControlSocket::OnSftpEvent(sftp_message const& message)。这时我们发现根据【Filezilla】 dispatch函数重载的例子-CSDN博客中dispatch重载的代码handler函数传入的参数是CSftpEvent * ev,如何变成了一个 sftp_message const& message ?
注意到CSftpEvent是一个分支上fz::simple_event模版类(在第一段代码里),而simple_event的解析如下。关键是因为 CSftpEvent ::v_
是一个 std::tuple<sftp_message>
,fz::apply()
会自动解包为sftp_message。
template<typename UniqueType, typename...Values>
class simple_event final : public event_base
{
public:
typedef UniqueType unique_type;
typedef std::tuple<Values...> tuple_type;
using event_base::event_base;
template<typename First_Value, typename...Remaining_Values>
explicit simple_event(First_Value&& value, Remaining_Values&& ...values)
: v_(std::forward<First_Value>(value), std::forward<Remaining_Values>(values)...)
{
}
/// \brief Returns a unique id for the type such that can be used directly in derived_type.
inline static size_t type() {
// Exporting templates from DLLs is problematic to say the least. It breaks
// ODR, so we use this trick that goes over the type name.
static size_t const v = get_unique_type_id(typeid(UniqueType*));
return v;
}
/// \brief Simply returns \ref type()
virtual size_t derived_type() const override {
return type();
}
/** \brief The event value, gets built from the arguments passed in the constructor.
*
* You don't need to access this member directly if you use the \ref dispatch mechanism.
*/
mutable tuple_type v_;
};
这是一个泛型事件类,它:
-
继承自
event_base
(这是 FileZilla 所有事件的基类); -
用模板参数构造出不同的事件类型;
-
用
std::tuple<Values...>
存储事件内容; -
用
UniqueType
生成类型唯一标识。
你可以理解为:这是 C++ 中的 type-safe 事件封装器。
好啦,这就解释清楚了消息message是怎么来的,这对于sftp协议也是很重要的一点。在处理sftp事件时,FileZilla和fzsftp通信时利用了下面的函数:
static int fznotify(sftpEventTypes type)
{
fprintf(stdout, "%c", (int)type + '0');
fflush(stdout);
return 0;
}
static int fznotify1(sftpEventTypes type, int data)
{
fprintf(stdout, "%c%d\n", (int)type + '0', data);
fflush(stdout);
return 0;
}
其中的sftpEventType就是message.type的类型啦。
enum class sftpEvent {
Unknown = -1,
Reply = 0,
Done,
Error,
Verbose,
Info,
Status,
Recv,
Send,
Listentry,
AskHostkey,
AskHostkeyChanged,
AskHostkeyBetteralg,
AskPassword,
Transfer,
RequestPreamble,
RequestInstruction,
UsedQuotaRecv,
UsedQuotaSend,
KexAlgorithm,
KexHash,
KexCurve,
CipherClientToServer,
CipherServerToClient,
MacClientToServer,
MacServerToClient,
Hostkey,
io_size,
io_open,
io_nextbuf,
io_finalize,
count
};