Remote Call Framework (RCF) 是一个很好用的ipc通信框架。
但在使用时有以下注意事项:
- 参加通信的程序可以是win64和win32混合,但不能是Unicode 和Ansii混合,否则可能无法收到函数调用(应该是跟字符串参数有关)。
- RCF::RcfInit 虽然在示例代码中全局只初使化一次。但如果是dll中,这个初使化就很讲究了。
- 不能作为全局变量,因为它内部会开启一个新线程并且等线程启动成功,而dll被加载时无法等待另一个线程。(卡住的调用堆栈见下图)
- Rcf作为静态库被主进程和DLL模块同时引用时,主程序中的init对子进程无效。
- Rcf作为共享库被主进程和DLL模块同时引用时,主程序中的init对子进程生效。
- Rcf作为静态库被DLL模块引用时,在A线程中初使化,B线程中创建的订阅也能接收到消息。
- Rcf作为共享库被DLL模块引用时,在A线程中初使化,B线程中创建的订阅也能接收到消息。
管理员和普通进程通信时权限
如果是使用tcp等网络模式通信,在管理员和普通进程之间通信并没有什么问题。但是如果是使用命名管道的模式在管理员和普通进程之间通信,就需要设置合适的权限,否则非管理员权限的进程打不开管理员权限的进程创建的命名管道。
// 设置权限,使非管理员权限的程序能连接上来
{
auto& transport = m_server->getServerTransport();
if (transport.getTransportType() == RCF::TransportType::Tt_Win32NamedPipe)
{
auto& win32NamedPipeTransport = dynamic_cast<RCF::Win32NamedPipeServerTransport&>(transport);
win32NamedPipeTransport.setSecurityAttributes(CreateEveryoneACL());
}
}
日志
Init之后, 使用RCF::enableLogging(); 开启日志。
如果想修改level,可以使用:RCF::LogManager::instance().DefaultLoggerPtr->setLevel(RCF::LogLevel_4);
或者新建Logger添加到 RCF::LogManager::instance()
中。
callback
RCF所能支持的callback的文档很有限。
server.setOnCallbackConnectionCreated
server.setOnCallbackConnectionCreated([](auto, auto) {
std::cout << "OnCallbackConnectionCreated" << std::endl;
});
本来以为这个是RcfServer端接收到新连接后会调用,但实测这段代码不会被触发,查阅代码起来跟 RCF::createCallbackConnection()
相关,但是也没有中断到代码。
这个回调的第一个参数是RcfSessionPtr,第二个是ClientTransportUniquePtr。但ClientTransportUniquePtr仅在作为客户端时才会创建(在ClientStub::instantiateTransport中使用mEndpoint->createClientTransport()触发)。而RcfSession是在作为服务器时会创建,在AsioNetworkSession::onAcceptCompleted
中由代码mTransport.getSessionManager().createSession()
创建。
再仔细看代码可能跟ProxyEndpoint模式有关,在这种情况下会有client和session的转换,但我不需要,不继续深究了,初使化运作就放在第一个被调用的函数内就行了。
订阅者回调
在项目中需要添加订阅端连接断开时回调,使用以下代码:
subscriptionPtr->getRcfSessionPtr()->setOnDestroyCallback([weakThis](auto& session)
{
FW_DEBUG(_T("[GameCore] 订阅连接已断开."));
if (const auto pThis = weakThis.lock())
{
pThis->m_impl->m_needReconnect = true;
}
});