eProsima RPC over DDS User Manual翻译,第3章

文章详细介绍了使用eProsimaFastRPC库开发远程程序调用的过程,包括通过IDL定义远程程序集、生成C++代码、定义简单及复杂类型、参数、程序、异常、接口和模块。此外,还阐述了服务端和客户端的实现,以及如何处理异常和错误。示例展示了如何定义和使用IDL文件,以及生成支持代码的工具rpcddsgen的使用方法。
摘要由CSDN通过智能技术生成

3. 构建一个应用程序

eProsima Fast RPC能让用户很容易的使用远端程序调用生成他们的发布程序,而且不用担心请求和应答是如何产生的以及如何通过网络发送的。本章更详细介绍上述四步开发顺序。经过实验的用户还需要参考第 4 节,其中涵盖了库的更高级主题。

3.1 定义一个远端程序集

IDL 用于定义服务器提供的远程程序,以及用作它们使用的参数数据类型。IDL 的结构基于 OMG IDL,在以下图中进行了描述:

eProsima Fast RPC 提供一个Java工具rpcddsgen, 用于从IDL文件为定义的远端程序集生成C++代码。使用选项和参数在第 3.2节(生成特定的远端程序调用支持代码)中指定。

3.1.1 IDL语法和映射到C++

用户通过IDL定义的类型遵循以下规则映射到C++11本地类型。

3.1.1.1 简单类型

eProsima Fast RPC 支持多种简单类型,开发者可将它们用作参数、返回值和复杂类型的成员。下表显示支持的简单类型如何在IDL中定义的,以及fastrpcgen是怎样将它们映射到C++11的。

        TablE 1: Specifying Simple Types in IDL for C++ USING C++11 NATIVE TYPES

IDL Type

Sample in IDL File

Sample Output Generated by rpcddsgen

char

char char_member

char char_member

wchar

wchar wchar_member

wchar_t wchar_member

octet

octet octet_member

uint8_t octet_member

short

short short_member

int16_t short_member

unsigned short

unsigned short ushort_member

uint16_t ushort_member

long

long long_member

int32_t long_member

unsigned long

unsigned long ulong_member

uint32_t ulong_member

long long

long long llong_member

int64_t llong_member

unsigned long long

unsigned long long ullong_member

uint64_t ullong_member

float

float float_member

float float_member

double

double double_member

double double_member

boolean

boolean boolean_member

bool boolean_member

bounded string

string<20> string_member

std::string string_member
/* maximum length = (20) */

unbounded string

string string_member

std::string string_member
/* maximum length = (255) */

3.1.1.2 复杂类型

复杂类型可通过结合简单类型创建。这些复杂类型可用作参数和返回值。下图显示支持的复杂类型是如何在IDL中定义的,以及fastrpcgen是如何将它们匹配到C++11的。

Table 2: Specifying Complex Types in IDL for C++ USING C++11 NATIVE TYPES

IDL Type

Sample in IDL File

Sample Output Generated by rpcddsgen

enum

enum PrimitiveEnum {
ENUM1,
ENUM2,
ENUM3
};

enum PrimitiveEnum {
ENUM1 = 10,
ENUM2
= 20,
ENUM3
= 30};(使rpcddsgen生成时,指定初始值会报错)

enum PrimitiveEnum : uint32_t {
ENUM1,
ENUM2,
ENUM3
};

enum PrimitiveEnum : uint32_t {
ENUM1 = 10,
ENUM2
= 20,
ENUM3
= 30
};

struct

struct PrimitiveStruct {
char char_member;
};

class PrimitiveStruct {

public:

/** Constructors **/
PrimitiveStruct();

...

/** Getter and Setters **/

char char_member();

void char_member(char x);

...

private:

char m_char_member;
};

union

union PrimitiveUnion switch(long) {
case 1:
short short_member;
default:
long long_member;
};

class PrimitiveUnion {

public:

/** Constructors **/
PrimitiveStruct();

...

/** Discriminator **/

int32_t _d();

void _d(int32_t x);

...

/** Getter and Setters **/
int16_t short_member();

int32_t long_member();

...

private:

int32_t m__d;

int16_t m_short_member;

int32_t m_long_member;
};

typedef

typedef short TypedefShort;

typedef int16_t TypedefShort;

array
(See note below)

struct OneDArrayStruct {
short short_array[2];
};

struct TwoDArrayStruct {
short short_array[1][2];
};

class OneDArrayStruct {

...

private:
std::array<int16_t, 2>

m_short_array;
};

class TwoDArrayStruct {

...

private:
std::array<std::array<int16_t, 2>, 1> m_short_array;
};

bounded sequence
(See note below)

struct SequenceStruct {
sequence<short,4>
short_sequence
;
};

class SequenceStruct {

...

private:
std::vector<int16_t>

m_short_sequence;
};

unbounded sequence
(See note below)

struct SequenceStruct {
sequence<short>
short_sequence
;
};

class SequenceStruct {
...

private:
std::vector<int16_t>

m_short_sequence;
};

Note: 这些复杂类型不能直接用作程序的参数,若要作为参数必须用typedef重新定义他们。

3.1.1.3 参数定义

三个保留字用来定义程序的参数。必须在每个参数中使用其中的一个。下表中展示了这些保留字和它们的含义。

Reserved word

Meaning

in

参数是入参。

inout

参数既是入参也是出参。

output

参数是出参。

例如,假定支持的类型T定义为参数。如果参数使用保留字in并且T是简单类型或枚举类型,则该类型作为T匹配到C++。如果类型 T 是复杂类型,则该类型在 C++ 中映射为 const T&.

如章节3.1.1.2 (复杂类型)提到的那样,数组和序列类型不能直接定义成参数类型。为了这样使用,必须事先用typedef重定义。

3.1.1.4 程序定义

程序的定义由两个或更多的元素组成:

  • 返回值的类型。允许使用void类型。

  • 程序名。

  • 参数列表,可以为空。

下面展示了应如何定义程序的示例:

long funcName(in short param1, inout long param2);

rpcddsgen按照下列规则匹配程序:

  • C++返回值的类型与 IDL 文件中定义的类型相同,使用 3.1.1.1 (简单类型) 和 3.1.1.2(复杂类型)章节中描述的表进行映射。

  • C++函数的名称与 IDL 文件中定义的函数的名称相同。

  • C++函数中的参数顺序与IDL文件中的参数顺序相同。C++中匹配的参数在章节3.1.1.3(参数定义)中描述。

遵循这些规则,之前的示例将产生如下的C++函数:

int32_t funcName(int16_t param1, int32_t& param2);

3.1.1.5 异常定义

IDL函数可以引发用户定义的异常,以指示错误的发生。一个异常结构可以包括若干个字段。下面展示了一个异常结构的示例:

exception ExceptionExample
{
long count;
string msg;
};

该示例产生如下的C++异常:

class ExceptionExample: public eprosima::rpc::exception::UserException

{

public:

ExceptionExample();

ExceptionExample(const ExceptionExample &ex);

ExceptionExample(ExceptionExample&& ex);

ExceptionExample& operator=(const ExceptionExample &ex);

ExceptionExample& operator=(ExceptionExample&& ex);

virtual ~ExceptionExample() throw();

virtual void raise() const;

/** Getters and Setters **/

int32_t count() const;

int32_t& count();

void count(int32_t _count);

...

private:

/** Exception members **/

int32_t m_count;

std::string m_msg;

};

若要指定操作可以引发一个或多个用户定义的异常,必须定义异常,并且必须将 IDL 引发子句添加到操作定义中。示例如下所示:

exception Exception1
{
long count;
};

exception Exception2
{

string msg;
};

void exceptionFunction()

raises(Exception1, Exception2);

3.1.1.6 接口定义

服务端提供的远端程序集必须在 IDL 接口中定义。下面显示了如何定义接口的示例:

interface InterfaceExample

{

// Set of remote procedures.

};

IDL接口匹配三个类:

  • InterfaceExampleProxy:本地服务端代理为客户端应用提供远端程序。客户端应用程序创建此类的一个对象并调用远端程序。

  • InterfaceExampleServerImpl:服务端使用,包含远端程序的定义。这些定义必须被用户实现。eProsima Fast RPC 创建此类的一个对象。

  • InterfaceExampleServer: 服务端的实现。用与执行实例。

如下例所示,eProsima RPC over DDS 支持接口继承:IDL接口ChildInterface有两个函数,从ParentInterface继承一个,它自己一个。

interface ParentInterface

{

void function1();

};

interface ChildInterface : ParentInterface

{

void function2();

};

3.1.1.7模块定义

为了将例如复杂类型、异常、函数和接口等相关定义合并成组,开发者可使用模块:

module ModuleExample

{

// Set of definitions

};

模块作为命名空间映射到C++,其中的每个定义都将被定义。

3.1.1.8 局限性

rpcddsgen 对 IDL 语法有一些限制:

  • 两个程序不能同名。

  • 程序中定义的复杂类型(数组和序列)必须事先用关键字typedef定义,如 CORBA IDL 2.0 规范强制的那样。

3.1.2示例

此示例将用作随后章节其他示例的基础。上一节中描述的 IDL 语法通过一个示例显示:

// file Bank.idl

enum ReturnCode

{

SYSTEM_ERROR,

ACCOUNT_NOT_FOUND,

AUTHORIZATION_ERROR,

NOT_MONEY_ENOUGH,

OPERATION_SUCCESS

};

struct Account

{

string AccountNumber;

string Username;

string Password;

}; // @top level false

interface Bank

{

ReturnCode deposit(in Account ac, in long money);

};

3.2生成特定的远端程序调用支持代码

描述API接口的IDL文件完成后,将产生客户端代理和服务端框架代码。fastrpcgen匹配IDL文件并产生相应的支持代码。

​​​​​​​3.2.1RPCDDSGEN命令语法:

使用以下语法调用该工具:

rpcddsgen [options] <IDL file> <IDL file> ...

Options:选项:

Option

Description

-help

显示帮助信息。

-version

显示当前eProsima RPC over DDS的版本。

-ppPath <directory>

C/C++预处理器的位置。

-ppDisable

禁用 C/C++ 预处理器。在不使用宏或包含时很有用。

-replace

替换已有的生成文件。

-example <platform>

为特定平台创建解决方案。开发人员将使用此解决方案来编译客户端和服务器。

可能的值: i86Win32VS2010, x64Win64VS2010, i86Linux2.6gcc4.4.5, x64Linux2.6gcc4.4.5

-d <path>

为生成文件设置输出目录。

-t <temp dir>

将特定的目录设置为临时目录。

-transport <transport>

选择生成代码将使用的DDS传输协议。可能的值:rtps, rti。默认值:rpts

-topicGeneration <option>

定义DDS主题是如何产生的。

可能的值: byInterface, byOperation. 默认值:byInterface

rpcddsgen产生若干个文件,如本节所述。它们的名字匹配 IDL 文件名,在本手册中表示为<IDLName>标记。

3.2.2服务端

rpcddsgen产生C++头文件和源文件,其中包括远端程序的声明和定义。这些文件是实现定义的接口的框架。开发者可使用这些源文件中的定义去实现他们远端程序的行为。这些文件包括<IDLName>ServerImpl.h和<IDLName>ServerImpl.cxx。fastrpcgen同样产生C++源文件并包含一个服务端程序和实例的示例。文件是<IDLName>ServerExample.cxx。

3.2.3 客户端

rpcddsgen同样产生C++源文件并带有一个客户端应用程序示例,以及该客户端应用程序怎样从服务端调用远端程序。文件是<IDLName>ClientExample.cxx。

3.3 服务端实现

运行rpcddsgen后,产生两个名为<IDLName> ServerImpl.cxx和<IDLName> ServerImpl.h的文件。这些文件与服务端提供的接口的框架相符合。远端程序定义在这些文件中,并且它们的每个行为都需要开发者去实现。对于 3.1.2(示例)中看到的远端程序deposit函数,可能的实现是:

ReturnCode BankServerImplExample::deposit(/*in*/ const Account& ac, /*in*/ int32_t money)

{

ReturnCode return_ = ::SYSTEM_ERROR;

return return_;

}

在执行这些程序实现时,应考虑以下几点

  • 开发者可以使用 in 参数,但无法释放其分配的内存,也无法释放其任何成员的。

  • 开发者可以修改inout参数。但是在给它们的成员分配内存时,以前分配的内存必须被释放。

  • out参数不能被初始化。开发者需要初始化它们。

fastrpcgen生成的代码也包含服务端使用的类。这些类在文件<IDLName>Server.h和<IDLName>Server.cxx中实现。

当创建一个类<IDLName>Server的对象时,可使用代理与其建立连接。创建连接的方式以及代理查找服务器的方式取决于所选的网络传输,如4.1 所述。

3.3.1API

使用3.1.2中的IDL示例,为这个类产生的API如下:

class BankServer: public eprosima::rpc::server::Server

{

public:

BankServer(

eprosima::rpc::strategy::ServerStrategy &strategy,

eprosima::rpc::transport::ServerTransport &transport,

eprosima::rpc::protocol::BankProtocol &protocol,

account_accountNumberResourceServerImpl &servant

);

virtual ~BankServer();

...

};

服务端提供了一个四个参数的构造函数。strategy 参数是一种服务策略,该策略定义服务端如何管理传入请求。服务策略在章节4.4(线程服务策略)中描述。

第二个参数是用于连接客户端代理的网络传输类型。第三个参数是传输协议。它由fastrpcgen生成,它的类负责反序列化收到的数据并将其转发给用户实现。最后,第四个参数是用户实现的服务端框架。

​​​​​​​3.3.2 异常和错误管理

在服务端,开发者可以通知远端程序执行中的错误。可在程序代码中抛出 eprosima::rpc::exception::ServerInternalException异常。然后,此异常将传递到代理,并将在客户端抛出。章节3.4.2解释了在客户端怎样处理异常。下面显示了异常如何工作的示例:

ReturnCode BankServerImpl::deposit(/*in*/const Account& ac, /*in*/ int32_t money)

{

ReturnCode returnedValue = SYSTEM_ERROR;

throw eprosima::rpc::exception::ServerInternalException(“Error in deposit procedure”);

return returnedValue;

}

3.3.3 示例

使用3.1.2 示例章节建议的IDL,开发者可以创建如下的服务端:

unsigned int threadPoolSize = 5;

ThreadPoolStrategy *pool = NULL;
BankProtocol *protocol = NULL;
RTPSServerTransport *transport = NULL;

BankServer *server = NULL;
BankServerImplExample servant;

try

{

pool = new ThreadPoolStrategy(threadPoolSize);

transport = new RTPSServerTransport("BankService", "Instance");

protocol = new BankProtocol();

server = new BankServer(*pool, *transport, *protocol, servant);

server->serve();

}

catch(eprosima::rpc::exception::InitializeException &ex)

{

std::cout << ex.what() << std::endl;

}

3.4 客户端实现

由fastrpcgen生成的代码包含充当远程服务端代理的类。这些类在文件<IDLName>Proxy.h和<IDLName>Proxy.cxx中实现。这些代理提供服务资源以便客户端应用程序能直接调用它的远端程序。

3.4.1 API

使用3.1.2 章节建议的IDL,类的API如下:

class BankProxy : public eprosima::rpc::proxy::Proxy

{

public:

BankProxy(eprosima::rpc::transport::ProxyTransport &transport,

eprosima::rpc::protocol::BankProtocol &protocol);

virtual ~BankProxy();

ReturnCode deposit(/*in*/ const Account& ac, /*in*/ int32_t money);

void deposit_async(Bank_depositCallbackHandler &obj, /*in*/ const Account& ac, /*in*/ int32_t money);

};

代理程序提供了一个构造函数并期望网络传输类对象作为它的第一个参数。第二个参时是协议对象。与服务端计数部分相同,它也是由fastrpcgen生成,它的职责是序列化和反序列化协议数据信息。

代理程序提供向客户端公开远端程序。在示例 IDL ,是deposit

3.4.2 异常

当执行远端程序调用时,可能会发生错误。这时,异常用来报告错误。当远端程序被调用时,下列异常可能会抛出:

Exception

Description

eprosima::rpc::exception::ClientInternalException

当客户端存在问题时此异常将会抛出。

eprosima::rpc::exception::ServerTimeoutException

当超过等待服务端回复的最长时间时,将抛出此异常。

eprosima::rpc::exception::ServerInternalException

当服务端存在问题时此异常将会抛出。

eprosima::rpc::exception::ServerNotFoundException

当代理找不到服务端时,此异常将会抛出。

所有的异常来自同一个基类: eprosima::rpc::exception::Exception.

3.4.3 示例

使用3.1.2 章节建议的IDL,开发者可按照以下方式访问deposit远端程序:

BankProtocol *protocol = NULL;

RTPSProxyTransport *transport = NULL;

BankProxy *proxy = NULL;

try {

protocol = new BankProtocol();

transport = new RTPSProxyTransport("BankService", "Instance");

proxy = new BankProxy(*transport, *protocol);

}

catch(eprosima::rpc::exception::InitializeException &ex) {

std::cout << ex.what() << std::endl;

}

Account ac;

int32_t money;

ReturnCode depositRetValue;

try {

depositRetValue = proxy->deposit(ac, money);

}

catch(eprosima::rpc::exception::Exception &ex) {

std::cout << ex.what() << std::endl;

}​​​​​​​

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值