必要條件
本教學課程以 HLCPP 入門教學課程為基礎。
總覽
在 Fuchsia 上使用 FIDL 的常見做法是透過通訊協定自行傳送通訊協定。確切來說,許多訊息會包含用戶端端或管道的伺服器端,管道會使用特定通訊協定進行通訊。在這種情況下,用戶端代表管道的遠端端會實作指定的通訊協定,而伺服器端則代表遠端端正在針對指定的通訊協定提出要求。用戶端和伺服器端的另一個條款是通訊協定和通訊協定要求。
本教學課程涵蓋:
- 這些用戶端和伺服器的使用會在 FIDL 及 HLCPP FIDL 繫結中結束。
- 要求管道模式及其好處。
本教學課程的完整程式碼範例位於 //examples/fidl/hlcpp/request_pipelining
。
FIDL 通訊協定
為此,本教學課程實作 fuchsia.example 程式庫中的 EchoLauncher
通訊協定:
@discoverable
closed protocol EchoLauncher {
strict GetEcho(struct {
echo_prefix string:MAX_STRING_LENGTH;
}) -> (resource struct {
response client_end:Echo;
});
strict GetEchoPipelined(resource struct {
echo_prefix string:MAX_STRING_LENGTH;
request server_end:Echo;
});
};
這是一種通訊協定,可讓用戶端擷取 Echo
通訊協定的執行個體。用戶端可以指定前置字串,而產生的 Echo
執行個體會將該前置字串新增至每個回應中。
為此,您可以使用以下兩種方法:
GetEcho
:將前置字串做為要求,並以連結至Echo
通訊協定實作之管道的用戶端回應。透過回應接收用戶端之後,用戶端即可開始使用用戶端在Echo
通訊協定上發出要求。GetEchoPipelined
:取得管道的伺服器端做為要求參數,並將Echo
的實作繫結至該要求。系統會假設提出要求的用戶端已接待用戶端,並在呼叫GetEchoPipeliend
後開始在該管道發出Echo
要求。
顧名思義,後者使用稱為通訊協定要求管道的模式,建議您採用。本教學課程將實作這兩種方法。
實作伺服器
實作 Echo 通訊協定
這個 Echo
實作可讓您指定前置字串,以區分 Echo
伺服器的不同的執行個體:
class EchoImpl : public fuchsia::examples::Echo {
public:
explicit EchoImpl(std::string prefix) : prefix_(prefix) {}
void EchoString(std::string value, EchoStringCallback callback) override {
std::cout << "Got echo request for prefix " << prefix_ << std::endl;
callback(prefix_ + value);
}
void SendString(std::string value) override {}
const std::string prefix_;
};
由於用戶端只會使用 EchoString
,因此 SendString
處理常式沒有任何內容。
實作 EchoLauncher 通訊協定
這個類別會使用繫結集來追蹤其啟動的所有 Echo
例項:
class EchoLauncherImpl : public fuchsia::examples::EchoLauncher {
public:
void GetEcho(std::string echo_prefix, GetEchoCallback callback) override {
std::cout << "Got non pipelined request" << std::endl;
fidl::InterfaceHandle<fuchsia::examples::Echo> client_end;
fidl::InterfaceRequest<fuchsia::examples::Echo> server_end = client_end.NewRequest();
bindings_.AddBinding(std::make_unique<EchoImpl>(echo_prefix), std::move(server_end));
callback(std::move(client_end));
}
void GetEchoPipelined(std::string echo_prefix,
fidl::InterfaceRequest<fuchsia::examples::Echo> server_end) override {
std::cout << "Got pipelined request" << std::endl;
bindings_.AddBinding(std::make_unique<EchoImpl>(echo_prefix), std::move(server_end));
}
fidl::BindingSet<fuchsia::examples::Echo, std::unique_ptr<fuchsia::examples::Echo>> bindings_;
};
程式碼不僅會明確指定繫結集範本所在的通訊協定,也會指定其儲存的繫結的指標類型。程式碼使用 unique_ptr
而非原始指標,因此繫結集擁有 EchoImpl
的執行個體。
以下是兩種方法的實作方式:
class EchoLauncherImpl : public fuchsia::examples::EchoLauncher {
public:
void GetEcho(std::string echo_prefix, GetEchoCallback callback) override {
std::cout << "Got non pipelined request" << std::endl;
fidl::InterfaceHandle<fuchsia::examples::Echo> client_end;
fidl::InterfaceRequest<fuchsia::examples::Echo> server_end = client_end.NewRequest();
bindings_.AddBinding(std::make_unique<EchoImpl>(echo_prefix), std::move(server_end));
callback(std::move(client_end));
}
void GetEchoPipelined(std::string echo_prefix,
fidl::InterfaceRequest<fuchsia::examples::Echo> server_end) override {
std::cout << "Got pipelined request" << std::endl;
bindings_.AddBinding(std::make_unique<EchoImpl>(echo_prefix), std::move(server_end));
}
fidl::BindingSet<fuchsia::examples::Echo, std::unique_ptr<fuchsia::examples::Echo>> bindings_;
};
如果是 GetEcho
,程式碼首先需要將管道的兩端執行個體化。這會使用伺服器端建立 Binding
,然後再與用戶端傳送回應。對於 GetEchoPipelined
,用戶端已經完成管道兩端的建立作業。它會保留一端,並傳遞至伺服器,因此所有程式碼都需要繫結至 Echo
實作。
提供 EchoLauncher 通訊協定
主要迴圈與伺服器教學課程相同,但會提供 EchoLauncher
而非 Echo
。
int main(int argc, const char** argv) {
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
EchoLauncherImpl impl;
fidl::Binding<fuchsia::examples::EchoLauncher> binding(&impl);
fidl::InterfaceRequestHandler<fuchsia::examples::EchoLauncher> handler =
[&](fidl::InterfaceRequest<fuchsia::examples::EchoLauncher> request) {
binding.Bind(std::move(request));
};
auto context = sys::ComponentContext::CreateAndServeOutgoingDirectory();
context->outgoing()->AddPublicService(std::move(handler));
std::cout << "Running echo launcher server" << std::endl;
return loop.Run();
}
建構伺服器
或者,您也可以確認設定正確無誤,請嘗試建立伺服器:
設定 GN 版本以納入伺服器:
fx set core.x64 --with //examples/fidl/hlcpp/request_pipelining/server:echo-server
建構 Fuchsia 映像檔:
fx build
實作用戶端
連線至 EchoLauncher
伺服器後,用戶端程式碼會使用 GetEcho
連線至 Echo
的一個執行個體,另一個則使用 GetEchoPipelined
連線至另一個執行個體,然後針對每個執行個體發出 EchoString
要求。
以下為非管道程式碼:
fuchsia::examples::EchoPtr echo;
auto callback = [&](fidl::InterfaceHandle<fuchsia::examples::Echo> client_end) {
std::cout << "Got non pipelined response" << std::endl;
echo.Bind(std::move(client_end));
echo->EchoString("hello!", [&](std::string response) {
std::cout << "Got echo response " << response << std::endl;
if (++num_responses == 2) {
loop.Quit();
}
});
};
echo_launcher->GetEcho("not pipelined: ", std::move(callback));
這個程式碼有兩個回呼層:
- 外層會處理啟動器要求。
- 內部層會處理
EchoString
要求。
此外,程式碼會將外部範圍內的 EchoPtr
例項化,然後在回呼內 Bind
進行例項,而不是呼叫 fidl::InterfaceRequest<T>::Bind
。這是因為在收到 echo 回應時,Proxy 必須在範圍內,而這種情況最可能在頂層回呼傳回之後。
儘管必須初始化管道,管道的程式碼卻非常簡單:
fuchsia::examples::EchoPtr echo_pipelined;
echo_launcher->GetEchoPipelined("pipelined: ", echo_pipelined.NewRequest());
echo_pipelined->EchoString("hello!", [&](std::string response) {
std::cout << "Got echo response " << response << std::endl;
if (++num_responses == 2) {
loop.Quit();
}
});
建立用戶端
或者,也可以嘗試建立用戶端:
設定 GN 版本以納入用戶端:
fx set core.x64 --with //examples/fidl/hlcpp/request_pipelining/client:echo-client
建構 Fuchsia 映像檔:
fx build
執行範例程式碼
fuchsia.examples.Echo
fuchsia.examples.EchoLauncher
設定建構以納入提供的套件,當中包含 echo 領域、伺服器和用戶端:
fx set core.x64 --with //examples/fidl/hlcpp:echo-launcher-hlcpp
建構 Fuchsia 映像檔:
fx build
執行
echo_realm
元件。這會建立用戶端和伺服器元件執行個體,並轉送功能:ffx component run /core/ffx-laboratory:echo_realm fuchsia-pkg://fuchsia.com/echo-launcher-hlcpp#meta/echo_realm.cm
啟動
echo_client
執行個體:ffx component start /core/ffx-laboratory:echo_realm/echo_client
伺服器元件會在用戶端嘗試連線至 EchoLauncher
通訊協定時啟動。您應該會在裝置記錄 (ffx log
) 中看到類似以下的輸出內容:
[echo_server][][I] Running echo launcher server
[echo_server][][I] Got non pipelined request
[echo_server][][I] Got pipelined request
[echo_server][][I] Got echo request for prefix pipelined:
[echo_client][][I] Got non pipelined response
[echo_client][][I] Got echo response pipelined: hello!
[echo_server][][I] Got echo request for prefix not pipelined:
[echo_client][][I] Got echo response not pipelined: hello!
根據列印順序,你會發現管道處理速度比較快。即使先傳送非管道的要求,管道案件的 echo 回應仍會先送達,因為要求管道會儲存用戶端與伺服器之間的往返作業。要求管道也能簡化程式碼。
如要進一步瞭解通訊協定要求管道,包括如何處理可能失敗的通訊協定要求,請參閱 FIDL API 評分量表。
終止領域元件以停止執行並清除元件執行個體:
ffx component destroy /core/ffx-laboratory:echo_realm