此文档为开发视界翻译转载者请注明出处(开发视界 www.sf.org.cn)否则追究法律责任 四、服务搜索 当得知了可用的设备和其地址的时候,客户端应用程序就向所提供的设备进行询问可提供什么服务。如果一个设备的可用的服务已经确定,客户端应用程序还将继续查询更多的信息。包括所提供的服务的属性。Symbian操作系统提供了服务搜索代理开发接口实现服务搜索。 本段将介绍Symbian蓝牙接口的很多新的部分。也将对第1章提到的定义作详细的解释。但是,本资料的目的是说明开发人员如何使用Symbian开发接口开发应用程序。关于蓝牙协议方面的信息,请参考http://www.bluetooth.org/spec/。
1、CSdpAgent 和 MSdpAgentNotifier 类 使用蓝牙在一个微型网中搜索服务是一件非常耗费时间的事情。Symbian操作系统提供了一个异步的开发接口用于执行服务的搜索。因为该客户端应用程序是异步的,因此要求客户端应用程序必须提供回调函数。这些函数将指向那些搜索功能函数。 Symbian系统的蓝牙接口提供了一个搜索类叫MSdpAgentNotifier,该类定义了一些搜索功能需要客户端应用程序来执行。 所有的搜索请求都提交给CSdpAgent类,这个类在btsdp.h头文件中定义。CSdpAgent的公共接口代码如下所示: class CSdpAgent : public CBase { public: IMPORT_C static CSdpAgent* NewL(MSdpAgentNotifier& aNotifier, const TBTDevAddr& aDevAddr); IMPORT_C static CSdpAgent* NewLC(MSdpAgentNotifier& aNotifier, const TBTDevAddr& aDevAddr); IMPORT_C ~CSdpAgent(); IMPORT_C void SetRecordFilterL(const CSdpSearchPattern& aUUIDFilter); IMPORT_C void SetAttributePredictorListL(const CSdpAttrIdMatchList& aMatchList); IMPORT_C void NextRecordRequestL(); IMPORT_C void AttributeRequestL(TSdpServRecordHandle aHandle, TSdpAttributeID aAttrID); IMPORT_C void AttributeRequestL(TSdpServRecordHandle aHandle, const CSdpAttrIdMatchList& aMatchList); IMPORT_C void AttributeRequestL(MSdpElementBuilder* aBuilder, TSdpServRecordHandle aHandle, TSdpAttributeID aAttrID); IMPORT_C void AttributeRequestL(MSdpElementBuilder* aBuilder, TSdpServRecordHandle aHandle, const CSdpAttrIdMatchList& aMatchList); IMPORT_C void Cancel(); … }; CSdpAgent创建的时候必须指定一个MSdpAgentNotifier 搜索对象和蓝牙设备的地址。这个地址就是客户端想要搜索的蓝牙地址。搜索类MSdpAgentNotifier 定义在btsdp.h头文件中。 1)在客户端应用程序开始搜索可用服务以前,必须先创建一个实例运行MSdpAgentNotifier接口 MSdpAgentNotifier这个函数在第二章中已经介绍,其用于指定一个服务的序号。在远程SDP数据库中存储的所有服务记录中,任何包含这些序号的服务都可以被搜索到。 而且,创建CSdpAgent类的实例必须保证确认远程蓝牙的地址和创建一个对象运MSdpAgentNotifier接口。 一个客户端应用程序可以使用CSdpAgent的SetRecordFilterL 功能对其感兴趣的服务进行设置。这个功能于CSdpSearchPattern 对象相关。
2、运行服务搜索 客户端应用程序只需要调用CSdpAgent的NextRecordRequestL函数即可启动搜索过程。这将启动一个异步的搜索。操作系统将告知应用程序过程启动MSdpAgentNotifier类。 当服务搜索完成的时候,将调用MSdpAgentNotifier::NextRecordRequestComplete函数。他的参数指定了一个错误代码(aError),一个服务基础句柄(aHandle)和一个整形值(aTotalRecordsCount)。错误代码将包含标准的Symbian操作系统错误代码中的一个比如KErrNOne。这说明搜索操作成功。服务记录句柄将记录符合搜索过滤参数的记录。最后,整型数将记录符合搜索条件的服务的数量。
3、运行属性查询 NextRecordRequestComplete 启动的时候,客户端应用程序将向远程设备询问更多服务记录信息。 CSdpAgent的AttributeRequestL 函数代表属性搜索的开始。AttributeRequestL函数将接受或者搜索ID,无论是一个还是多个。 如果要指定一个ID的集合,需要使用CSdpAttrIdMatchList类,在第二章中提到,这个类允许客户端应用程序指定ID的范围。 用于开始属性搜索的AttributeRequestL函数也是异步的。每次针对不同的参数寻找服务的时候,操作系统都将启动一次MSdpAgentNotifier::AttributeRequestResult函数。 当所有符合搜索参数的属性都查找到后,MSdpAgentNotifier::AttributeRequestComplete启动。
4、蓝牙对话 S60第三版SDK提供了一个蓝牙对话。在这个对话里面,CChatBtServiceSearcher实际上运行的是MSdpAgentNotifier接口。该内容定义在series60Ex\Chat\inc\ChatBtServiceSearcher.h 头文件中。 CChatBtServiceSearcher既运行MSdpAgentNotifier接口也包含CSdpAgent 对象。CChatBtServiceSearcher 既负责发布搜索请求,也处理操作系统的反馈。 CChatBtServiceSearcher 类的内容可以从series60Ex\Chat\src\ChatBtServiceSearcher.cpp 中找到。其代码的结构与下面的相似。 FindServiceL函数说明了搜索服务初始化的过程。 void CChatBtServiceSearcher::FindServiceL( TRequestStatus& aObserverRequestStatus ) { if ( !iResponse().IsValidBDAddr() ) { User::Leave( KErrNotFound ); } iHasFoundService = EFalse; // delete any existing agent and search pattern delete iSdpSearchPattern; iSdpSearchPattern = NULL; delete iAgent; iAgent = NULL; iAgent = CSdpAgent::NewL( *this, BTDevAddr() ); iSdpSearchPattern = CSdpSearchPattern::NewL(); iSdpSearchPattern->AddL( ServiceClass() ); // return code is the position in the list that the UUID is // inserted at and is intentionally ignored iAgent->SetRecordFilterL( *iSdpSearchPattern ); iStatusObserver = &aObserverRequestStatus; iAgent->NextRecordRequestL(); } CChatBtServiceSearcher::NextRecordRequestComplete函数用于处理服务记录查询的请求。 void CChatBtServiceSearcher::NextRecordRequestComplete(TInt aError, TSdpServRecordHandle aHandle, TInt aTotalRecordsCount) { 其首先确定应用程序并没有接受到所有匹配的记录,而且要确保查询没有错误。 if ( aError == KErrEof ) { Finished(); return; } if ( aError != KErrNone ) { iLog.LogL( KErrNRRCErr, aError ); Finished( aError ); return; } if ( aTotalRecordsCount == 0 ) { HBufC* errNRRCNoRecords = StringLoader ::LoadLC ( R_CHAT_ERR_NRRC_NO_RECORDS ); iLog.LogL( *errNRRCNoRecords ); CleanupStack::PopAndDestroy ( errNRRCNoRecords ); Finished( KErrNotFound ); return; } // Request its attributes 如果没有任何错误但是没有任何服务记录处理,应用程序将发送属性请求获取更多信息。 iAgent->AttributeRequestL( aHandle, KSdpAttrIdProtocolDescriptorList ); CChatBtServiceSearcher::AttributeRequestResult 用于处理原始的属性请求。他将分析属性值确定是否搜索到相关服务。 void CChatBtServiceSearcher::AttributeRequestResultL( TSdpServRecordHandle /*aHandle*/, TSdpAttributeID aAttrID, CSdpAttrValue* aAttrValue ) { __ASSERT_ALWAYS( aAttrID == KSdpAttrIdProtocolDescriptorList, User::Leave( KErrNotFound ) ); TChatSdpAttributeParser parser( ProtocolList(), *this ); // Validate the attribute value, and extract //the RFCOMM channel aAttrValue->AcceptVisitorL( parser ); if ( parser.HasFinished() ) { // Found a suitable record so change state iHasFoundService = ETrue; } } void CChatBtServiceSearcher::AttributeRequestComplete( TSdpServRecordHandle aHandle, TInt aError ) { TRAPD(error,AttributeRequestCompleteL( aHandle, aError )); if ( error != KErrNone ) { Panic(EChatBtServiceSearcherAttributeRequestComplete); } }
|