网上搜索关于mojo教程 多数都是理论 加上翻译谷歌mojo文档的,但是如何自定义两个进程使用mojo通信呢?看下面的完整例子介绍:(本人也是参考谷歌代码例子改编而成)
本文演示了client.exe和service.exe 通过mojo::IncomingInvitation模式进行通信使用例子。
废话不多说直接上代码:
一、目录结构如下图:
按照如图所示目录结构添加文件即可。
二、定义sample.mojom接口
1、demo\logger\public\mojom\sample.mojom
module sample.mojom;
interface Logger {
Log(string message);
GetTail() => (string message);
};
2、demo\logger\public\mojom\BUILD.gn
import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojom") {
sources = [
"sample.mojom",
]
}
3、gn gen out/debug 自动生成代码如下:
out\Debug\gen\demo\logger\public\mojom\sample.mojom.h
out\Debug\gen\demo\logger\public\mojom\sample.mojom.cc
其他更多参考out\Debug\gen\demo\logger\目录下
三、client.exe端代码:
1、demo\client\client.cc //client.exe main函数实现文件:
#include<iostream>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/memory_mapped_file.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "demo/logger/public/mojom/sample.mojom.h"
#include "demo/client/logger_test.h"
#include "demo/process_bootstrapper_helper.h"
#include "mojo/core/embedder/configuration.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/core/embedder/scoped_ipc_support.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"
mojo::ScopedMessagePipeHandle LaunchAndConnect() {
// Under the hood, this is essentially always an OS pipe (domain socket pair,
// Windows named pipe, Fuchsia channel, etc).
mojo::PlatformChannel channel;
mojo::OutgoingInvitation invitation;
// Attach a message pipe to be extracted by the receiver. The other end of the
// pipe is returned for us to use locally. We choose the arbitrary name "pipe"
// here, which is the same name that the receiver will have to use when
// plucking this pipe off of the invitation it receives to join our process
// network.
mojo::ScopedMessagePipeHandle pipe = invitation.AttachMessagePipe("pipe");
base::LaunchOptions options;
// This is the relative path to the mock "renderer process" binary. We pass it
// into `base::LaunchProcess` to run the binary in a new process.
static const base::CommandLine::CharType* argv[] = {
FILE_PATH_LITERAL("./service")};
base::CommandLine command_line(1, argv);
// Delegating to Mojo to "prepare" the command line will append the
// `--mojo-platform-channel-handle=N` command line argument, so that the
// renderer knows which file descriptor name to recover, in order to establish
// the primordial connection with this process. We log the full command line
// next, to show what mojo information the renderer will be initiated with.
channel.PrepareToPassRemoteEndpoint(&options, &command_line);
LOG(INFO) << "Browser: " << command_line.GetCommandLineString();
base::Process child_process = base::LaunchProcess(command_line, options);
channel.RemoteProcessLaunchAttempted();
mojo::OutgoingInvitation::Send(std::move(invitation), child_process.Handle(),
channel.TakeLocalEndpoint());
return pipe;
}
logger_test *g_logger_test = nullptr;
void CreateProcessRemote(mojo::ScopedMessagePipeHandle pipe,
const std::string& msg) {
mojo::PendingRemote<sample::mojom::Logger> pending_remote(std::move(pipe),
0u);
mojo::Remote<sample::mojom::Logger> remote(std::move(pending_remote));
g_logger_test = new logger_test(std::move(remote));
LOG(INFO) << "Browser invoking SayHello() on remote pointing to renderer";
g_logger_test->sendLoggerMsg(msg);
g_logger_test->sendLoggerExt();
}
int main(int argc, const char* argv[]) {
base::AtExitManager at_exit_manager;
base::CommandLine::Init(argc, argv);
ProcessBootstrapperHelp bootstrapper;
bootstrapper.InitMainThread(base::MessagePumpType::IO);
bootstrapper.InitMojo(/*as_browser_process=*/true);
mojo::ScopedMessagePipeHandle pipe = LaunchAndConnect();
std::string msg("jd test");
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&CreateProcessRemote, std::move(pipe),msg));
base::RunLoop run_loop;
// Delay shutdown of the browser process for visual effects, as well as to
// ensure the browser process doesn't die while the IPC message is still being
// sent to the target process asynchronously, which would prevent its
// delivery.
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(
[](base::OnceClosure quit_closure) {
LOG(INFO) << "'Browser process' shutting down";
std::move(quit_closure).Run();
},
run_loop.QuitClosure()),
base::Seconds(2));
run_loop.Run();
delete g_logger_test;
g_logger_test = nullptr;
return 0;
}
2、demo\client\logger_test.cc
#include "demo/client/logger_test.h"
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <iostream>
#include "base/functional/bind.h"
logger_test::logger_test(mojo::Remote<sample::mojom::Logger> rc)
: logger_client_(std::move(rc)) {}
logger_test::~logger_test() {
std::cout << "~logger_test()\n";
}
void logger_test::sendLoggerMsg(const std::string& msg) {
logger_client_->Log(msg);
}
void logger_test::GetTailCallbackTest(const std::string& msg){
LOG(ERROR) << "GetTail callback " << msg;
}
void logger_test::sendLoggerExt(){
logger_client_->GetTail(base::BindOnce(&logger_test::GetTailCallbackTest,
base::Unretained(this)));
}
3、demo\client\logger_test.h
#ifndef CHROME_BROWSER_LOGGER_TEST_H_
#define CHROME_BROWSER_LOGGER_TEST_H_
#include <string>
#include <vector>
#include "base/logging.h"
#include "demo/logger/public/mojom/sample.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
class logger_test {
public:
logger_test(mojo::Remote<sample::mojom::Logger> rc);
~logger_test();
void sendLoggerMsg(const std::string& msg);
void sendLoggerExt();
private:
void GetTailCallbackTest(const std::string& msg);
/* data */
mojo::Remote<sample::mojom::Logger> logger_client_;
};
#endif // CHROME_BROWSER_LOGGER_TEST_H_
四、service.exe端代码:
1、demo\service\service.cc //service.exe main函数实现文件:
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <iostream>
#include <initializer_list>
#include <memory>
#include <string>
#include <tuple>
#include <vector>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/memory_mapped_file.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "demo/process_bootstrapper_helper.h"
#include "demo/service/LoggerImpl.h"
#include "mojo/core/embedder/configuration.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/core/embedder/scoped_ipc_support.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"
LoggerImpl* g_process_impl = nullptr;
void BindProcessImpl(mojo::ScopedMessagePipeHandle pipe) {
// Create a receiver
mojo::PendingReceiver<sample::mojom::Logger> pending_receiver(
std::move(pipe));
g_process_impl = new LoggerImpl(std::move(pending_receiver));
}
int main(int argc, const char* argv[]) {
base::AtExitManager at_exit_manager;
base::CommandLine::Init(argc, argv);
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
std::wcout << command_line.GetCommandLineString().c_str()<<"\n";
ProcessBootstrapperHelp bootstrapper;
bootstrapper.InitMainThread(base::MessagePumpType::IO);
bootstrapper.InitMojo(/*as_browser_process=*/false);
// Accept an invitation.
//
// `RecoverPassedEndpointFromCommandLine()` is what makes use of the mojo
// platform channel handle that gets printed in the above `LOG()`; this is the
// file descriptor of the first connection that this process shares with the
// browser.
mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept(
mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
*base::CommandLine::ForCurrentProcess()));
// Extract one end of the first pipe by the name that the browser process
// added this pipe to the invitation by.
mojo::ScopedMessagePipeHandle pipe = invitation.ExtractMessagePipe("pipe");
base::RunLoop run_loop;
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&BindProcessImpl, std::move(pipe)));
run_loop.Run();
std::cout << "service\n";
return 0;
}
LoggerImpl.h 和LoggerImpl.cc 是sample::mojom::Logger 实现类:
2、demo\service\LoggerImpl.h
#ifndef CHROME_BROWSER_LOGGERIMPL_H_
#define CHROME_BROWSER_LOGGERIMPL_H_
#include <string>
#include <vector>
#include "base/logging.h"
#include "demo/logger/public/mojom/sample.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
class LoggerImpl : public sample::mojom::Logger {
public:
// NOTE: A common pattern for interface implementations which have one
// instance per client is to take a PendingReceiver in the constructor.
explicit LoggerImpl(mojo::PendingReceiver<sample::mojom::Logger> pending_receiver);
~LoggerImpl() override;
// sample::mojom::Logger:
void Log(const std::string& message) override;
void GetTail(GetTailCallback callback) override;
void OnError();
private:
mojo::Receiver<sample::mojom::Logger> receiver_;
std::vector<std::string> lines_;
LoggerImpl(const LoggerImpl&) = delete;
LoggerImpl& operator=(const LoggerImpl&) = delete;
};
#endif // CHROME_BROWSER_LOGGERIMPL_H_
3、demo\service\LoggerImpl.cc
#include "demo/service/LoggerImpl.h"
#include "base/functional/bind.h"
LoggerImpl::LoggerImpl(
mojo::PendingReceiver<sample::mojom::Logger> pending_receiver)
: receiver_(this, std::move(pending_receiver)) {
// receiver_.Bind(std::move(pending_receiver));
receiver_.set_disconnect_handler(
base::BindOnce(&LoggerImpl::OnError, base::Unretained(this)));
}
LoggerImpl::~LoggerImpl() {}
// sample::mojom::Logger:
void LoggerImpl::Log(const std::string& message) {
LOG(ERROR) << "[Logger] " << message;
lines_.push_back(message);
}
void LoggerImpl::GetTail(GetTailCallback callback) {
std::move(callback).Run(lines_.back());
}
void LoggerImpl::OnError() {
LOG(ERROR) << "Client disconnected! Purging log lines.";
lines_.clear();
}
五、初始化mojo和线程辅助类:
demo\process_bootstrapper_helper.h
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CODELABS_MOJO_EXAMPLES_PROCESS_BOOTSTRAPPER_HELPER_H_
#define CODELABS_MOJO_EXAMPLES_PROCESS_BOOTSTRAPPER_HELPER_H_
#include "base/message_loop/message_pump.h"
#include "base/run_loop.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/threading/thread.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/core/embedder/scoped_ipc_support.h"
class ProcessBootstrapperHelp {
public:
ProcessBootstrapperHelp();
~ProcessBootstrapperHelp();
// This sets up the main thread with a message pump of `type`, and optionally
// a dedicated IO thread if `type` is *not* `base::MessagePumpType::IO`.
void InitMainThread(base::MessagePumpType type) {
// Creates a sequence manager bound to the main thread with a message pump
// of some specified type. The message pump determines exactly what the
// event loop on its thread is capable of (i.e., what *kind* of messages it
// can "pump"). For example, a `DEFAULT` message pump is capable of
// processing simple events, like async timers and posted tasks. The `IO`
// message pump type — which is used in every example in this codelab — is
// capable of asynchronously processing IO over IPC primitives like file
// descriptors, used by Mojo. A thread with *that* kind of message pump is
// required for any process using Mojo for IPC.
std::unique_ptr<base::MessagePump> pump = base::MessagePump::Create(type);
sequence_manager =
base::sequence_manager::CreateSequenceManagerOnCurrentThreadWithPump(
std::move(pump),
base::sequence_manager::SequenceManager::Settings::Builder()
.SetMessagePumpType(type)
.Build());
default_tq = std::make_unique<base::sequence_manager::TaskQueue::Handle>(
sequence_manager->CreateTaskQueue(
base::sequence_manager::TaskQueue::Spec(
base::sequence_manager::QueueName::DEFAULT_TQ)));
sequence_manager->SetDefaultTaskRunner((*default_tq)->task_runner());
if (type == base::MessagePumpType::DEFAULT) {
InitDedicatedIOThread();
}
}
// Must be called after `InitMainThread()`.
void InitMojo(bool as_browser_process) {
CHECK(default_tq) << "Must call `InitMainThread()` before `InitMojo()`";
// Basic Mojo initialization for a new process.
mojo::core::Configuration config;
// For mojo, one process must be the broker process which is responsible for
// trusted cross-process introductions etc. Traditionally this is the
// "browser" process.
config.is_broker_process = as_browser_process;
mojo::core::Init(config);
// The effects of `ScopedIPCSupport` are mostly irrelevant for our simple
// examples, but this class is used to determine how the IPC system shuts
// down. The two shutdown options are "CLEAN" and "FAST", and each of these
// may determine how other processes behave if *this* process has a message
// pipe that is in the middle of proxying messages to another process where
// the other end of the message pipe lives.
//
// In real Chrome, both the browser and renderer processes can safely use
// `FAST` mode, because the side effects of quickly terminating the IPC
// system in the middle of cross-process IPC message proxying is not
// important. See this class's documentation for more information on
// shutdown.
//
// We initialize `ipc_support` with a task runner for whatever thread should
// be the IO thread. This means preferring `io_task_runner` when it is
// non-null, and the default task runner otherwise.
mojo::core::ScopedIPCSupport ipc_support(
io_task_runner ? io_task_runner : (*default_tq)->task_runner(),
mojo::core::ScopedIPCSupport::ShutdownPolicy::FAST);
}
std::unique_ptr<base::sequence_manager::TaskQueue::Handle> default_tq;
std::unique_ptr<base::sequence_manager::SequenceManager> sequence_manager;
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner;
private:
// Note that you cannot call this if you've ever called
// `InitMainThread(base::MessagePumpType::IO)` since that means the main
// thread *itself* the IO thread.
void InitDedicatedIOThread() {
io_thread_ = std::make_unique<base::Thread>("ipc!");
io_thread_->StartWithOptions(
base::Thread::Options(base::MessagePumpType::IO, 0));
io_task_runner = io_thread_->task_runner();
}
std::unique_ptr<base::Thread> io_thread_;
};
ProcessBootstrapperHelp::ProcessBootstrapperHelp() = default;
ProcessBootstrapperHelp::~ProcessBootstrapperHelp() = default;
#endif // CODELABS_MOJO_EXAMPLES_PROCESS_BOOTSTRAPPER_HELPER_H_
六、添加demo\build.gn文件:
1、 dem\BUILD.gn
# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/compiler/compiler.gni")
group("jdcode_mojo_examples") {
testonly = true
if (is_win) {
deps = [
":client",
":service",
]
}
}
executable("client") {
sources = [
"client/client.cc",
"client/logger_test.cc",
"client/logger_test.h",
"process_bootstrapper_helper.h",
]
if (is_win) {
ldflags = [ "/LARGEADDRESSAWARE" ]
}
deps = [
"//base",
"//build/win:default_exe_manifest",
"//demo/logger/public/mojom",
"//ipc",
"//mojo/core/embedder",
"//mojo/public/cpp/platform",
"//mojo/public/mojom/base",
]
}
executable("service") {
sources = [
"service/service.cc",
"service/LoggerImpl.h",
"service/LoggerImpl.cc",
"process_bootstrapper_helper.h",
]
if (is_win) {
ldflags = [ "/LARGEADDRESSAWARE" ]
}
deps = [
"//base",
"//build/win:default_exe_manifest",
"//demo/logger/public/mojom",
"//ipc",
"//mojo/core/embedder",
"//mojo/public/cpp/platform",
"//mojo/public/mojom/base",
]
}
2、src\BUILD.gn 追加如下:
if (is_win) {
deps += [
"//demo/logger/public/mojom",
"//demo:jdcode_mojo_examples",
]
}
七、开始编译和调试:
1、gn gen out/debug
自动生成mojo文件:
2、ninja -C out/debug client
编译client.exe
3、ninja -C out/debug service
编译service.exe
4、最终生成文件如图:
八、看下调试堆栈效果:
8.1)、 client.exe主要流程:
1、初始化线程和mojo // mojo::core::Init(config);
ProcessBootstrapperHelp bootstrapper;
bootstrapper.InitMainThread(base::MessagePumpType::IO);
bootstrapper.InitMojo(/*as_browser_process=*/true);
2、LaunchAndConnect();
2.1)、mojo::OutgoingInvitation建立并且启动service.exe[通过base::LaunchProcess],
2.2)将./service --mojo-platform-channel-handle=508传递给service.exe
3、CreateProcessRemote调用Log和GetTail接口函数
8.2)、service.exe主要流程:
1、初始化线程和mojo // mojo::core::Init(config);
ProcessBootstrapperHelp bootstrapper;
bootstrapper.InitMainThread(base::MessagePumpType::IO);
bootstrapper.InitMojo(/*as_browser_process=*/false);
2、与client.exe建立链接:
mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept(
mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
*base::CommandLine::ForCurrentProcess()));
// Extract one end of the first pipe by the name that the browser process
// added this pipe to the invitation by.
mojo::ScopedMessagePipeHandle pipe = invitation.ExtractMessagePipe("pipe");
3、BindProcessImpl(mojo::ScopedMessagePipeHandle pipe)处理客户端client.exe发出的函数请求Log和GetTail
8.3)、看下最终效果图:
总结:
本文演示了通过mojo::IncomingInvitation进行通信的完整例子,仅供参考,mojo基础和原理参考官网介绍。