目前支持蓝牙的手机大多会支持几个标准的蓝牙服务,比如OPP(object push profile),FTP(file transfer profile)什么的,都是两个设备之间用来相互传送资料的,也有一些蓝牙耳机、拨号上网服务,蓝牙允许用户自定义服务,以便对端设备来访问,他们都是工作于蓝牙RFCOMM层之上的,RFCOMM是一个串口仿真协议,这样可以把某一个蓝牙服务虚拟为一个串口方便程序的编写。比如在蓝牙配对完成后,PC首先会去查询对方的SDP(Service Discovery Application Profile)服务,这其中有所有对端(这里就是手机)支持的服务的详细信息,PC得到这个信息后就会显示给用户对方有哪些服务,用户可以自由的选择使用哪些服务。这里举例说明我们的DEBUG服务,PC发现了我们在手机上注册的这个DEBUG串口服务(我们注册的当然可以是串口服务,标准中叫他SPP),将其显示给用户,用户在选择连接,在蓝牙链路连接成功后PC会将其虚拟为一个PC上的串口设备,这样我们就可以通过这个串口给手机通讯了。
       现在看看我们要实现的这个功能,主要就是两个功能,一是创建一个蓝牙服务,能处理pc过来的连接请求,并建立蓝牙连接,断开后继续监听等待下一次连接,第二是提供一个send函数发数据就可以了。所以我将其分为一个父类CBtSvr来处理第一个问题,再写一个BtDbg子类来处理第二个问题。
       先看看CBtSvr需要实现的功能,用一个活动对象来实现

class CBtSvr : public public CActive 

    { 

public : 

       // 当前服务器的状态 

       enum CBtBaseSvrStat { 

              // 空闲状态 

              EWaitingToGetConnection, 

              // 监听状态 

              EGettingConnection, 

              // 连接状态 

              EInConnection, 

       }     ; 

  

       CBtSvr(); 

       virtual ~CBtSvr(){} ; 

       // 类似消息处理的主循环 

       virtual void RunL(); 

       // 启动服务器 

       virtual int StartL(); 

       // 关闭服务器 

       virtual void CloseL(); 

       // 取消当前提交的请求 

       virtual void DoCancel(); 

       // 返回服务器当先状态当前状态 

       inline CBtBaseSvrStat Status(void) { return iStat; }; 

  

protected: 

       // socket 服务器 

       RSocketServ sockSvr; 

       // 监听socket 

       RSocket listenSock; 

       // 连接 socket 

       RSocket connSock; 

       // send状态标志 

       TRequestStatus iSendStatus; 

       // 服务的当前状态 

       CBtBaseSvrStat iStat; 

       TSockXfrLength iLen; 

       TInt channelNum; 

       

protected: 

       // 绑定服务,留给子类实现 

       virtual int BindL(void) = 0; 

       // 监听 

       virtual int ListenL(void); 

       // 接受连接请求 

       virtual int AcceptL(void); 

       // 注册蓝牙服务中的Protocol段 

       virtual void BuildProtocolDescriptionL(CSdpAttrValueDES* aProtocolDescriptor, TInt aPort); 

       // 设置蓝牙服务安全 

       void SetSecurityOnChannelL(TBool aAuthentication, TBool aEncryption, TBool aAuthorisation, TInt aChannel); 

       // 注册蓝牙服务 

       int RegieterBlueToothServerL(const TDesC& KServiceName, TInt KSerialClassID); 

       // 绑定蓝牙服务名 

       int BuildSerivce(const TDesC& ServiceName, TInt KSerialClassID); 

}; 

构造函数,初始化状态并加入活动对象调度器 

CBtSvr::CBtSvr() 

       : iStat(EWaitingToGetConnection),CActive(0) 

{ 

       CActiveScheduler::Add(this); 

} 

开启蓝牙debug服务 

int CBtSvr::StartL() 

{ 

       // 如果当前状态不对或者已经提交了事件 

       if (iStat != EWaitingToGetConnection || IsActive()) { 

              return -1; 

       } 

       // 建立绑定debug服务 

       if (BindL() < 0) { 

              return -1; 

       } 

       // 开始监听 

       if (ListenL() < 0) { 

              listenSock.Close(); 

              sockSvr.Close(); 

              return -1; 

       } 

       // 接受连接 

       if (AcceptL() < 0) { 

              listenSock.Close(); 

              sockSvr.Close(); 

              return -1; 

       } 

       return 0; 

} 

// 监听函数 

int CBtSvr::ListenL(void) 

{ 

       assert(iStat == EWaitingToGetConnection); 

  

       // 只支持一个连接 

       if(listenSock.Listen(1) != KErrNone) { 

              return -1; 

       } 

       return 0; 

} 

// 提交接受连接请求 

int CBtSvr::AcceptL(void) 

{ 

       if (connSock.Open(sockSvr) != KErrNone) { 

              return -1; 

       }     

       listenSock.Accept(connSock, iStatus); 

       iStat = EGettingConnection; 

       SetActive(); 

       return 0; 

} 

// 实现CActive的doCancel函数供取消事件请求时调用 

void CBtSvr::DoCancel() 

{ 

       if (!IsActive()) 

              return; 

  

       switch (iStat) { 

       case EGettingConnection: 

              listenSock.CancelAll(); 

              break; 

       case EInConnection: 

              connSock.CancelAll(); 

              break; 

       default: 

              break; 

       } 

} 

// 服务器关闭函数 

void CBtSvr::CloseL() 

{ 

       Cancel(); 

       iStat = EWaitingToGetConnection; 

       listenSock.Close(); 

       sockSvr.Close(); 

} 

// 主事件循环 

void CBtSvr::RunL() 

{ 

       if (iStatus != KErrNone) { 

              // 出错处理 

              switch (iStat) { 

                     case EGettingConnection: 

                            iStat = EWaitingToGetConnection; 

                            break; 

  

                     case EInConnection: 

                            // 可能是对端断开连接 

                            iStat = EWaitingToGetConnection; 

                            // 重新提交接受连接事件 

                            AcceptL(); 

                            break; 

  

                     default: 

                            break; 

              } 

    } 

       else { 

              switch (iStat) { 

                     case EGettingConnection: 

                            // 连接建立成功 

                            iStat = EInConnection; 

                            break; 

  

                     case EInConnection: 

                            // 收到数据不做任何处理 

                            break; 

  

                     default: 

                            break; 

              } 

    } 

} 

  
// 这个函数是设置蓝牙服务安全,没有仔细研究,simple中搬出来
void CBtSvr::SetSecurityOnChannelL(TBool aAuthentication,
                          TBool aEncryption, 
                          TBool aAuthorisation,
                          TInt aChannel)
{
    const TUid KUidBTMobTimeObexAppValue = {0x0};
    // a connection to the security manager
    RBTMan secManager;
 
    // a security session
    RBTSecuritySettings secSettingsSession;
 
    // define the security on this port
    User::LeaveIfError(secManager.Connect());
    CleanupClosePushL(secManager);
    User::LeaveIfError(secSettingsSession.Open(secManager));
    CleanupClosePushL(secSettingsSession);
 
    // the security settings 
    TBTServiceSecurity serviceSecurity(KUidBTMobTimeObexAppValue, KSolBtRFCOMM, 0);
 
    //Define security requirements
    serviceSecurity.SetAuthentication(aAuthentication);
    serviceSecurity.SetEncryption(aEncryption); 
    serviceSecurity.SetAuthorisation(aAuthorisation);
 
    serviceSecurity.SetChannelID(aChannel);
    TRequestStatus status;
    secSettingsSession.RegisterService(serviceSecurity, status);
    
    User::WaitForRequest(status); // wait until the security settings are set
    User::LeaveIfError(status.Int());
    
    CleanupStack::PopAndDestroy(); // secManager
    CleanupStack::PopAndDestroy(); // secSettingsSession
}
// 注册蓝牙串口服务
int CBtSvr::RegieterBlueToothServerL(const TDesC& KServiceName, TInt KSerialClassID)
{
       // reg the sdp server database
       RSdp sdp;
       RSdpDatabase iSdpDatabase;
       TSdpServRecordHandle iRecord;
 
       // sdp服务器连接
       if(sdp.Connect() != KErrNone) {
              return -CNSE_SYS_ERR;
       }
       // 打开数据库
       if(iSdpDatabase.Open(sdp) != KErrNone) {
              return -CNSE_SYS_ERR;
       }
       // 创建一个服务
       iSdpDatabase.CreateServiceRecordL(KSerialClassID, iRecord);
 
       // add a Protocol to the record
       CSdpAttrValueDES* vProtocolDescriptor = CSdpAttrValueDES::NewDESL(NULL);
       CleanupStack::PushL(vProtocolDescriptor);
       // 设置protocl相关信息
       BuildProtocolDescriptionL(vProtocolDescriptor, channelNum);
 
       iSdpDatabase.UpdateAttributeL(iRecord, 
                                                         KSdpAttrIdProtocolDescriptorList, 
                                                         *vProtocolDescriptor);
       
       // Add 0x5 display 设置为可见
       CSdpAttrValueDES* browseGroupList = CSdpAttrValueDES::NewDESL(NULL);
       CleanupStack::PushL(browseGroupList);
       browseGroupList
       ->StartListL()   // List of protocols required for this method
                     ->BuildUUIDL(TUUID(TUint16(0x1002)))
       ->EndListL();
       iSdpDatabase.UpdateAttributeL(iRecord, KSdpAttrIdBrowseGroupList, *browseGroupList);
       CleanupStack::PopAndDestroy(2);
 
       // Add a name to the record,名字
       iSdpDatabase.UpdateAttributeL(iRecord, 
                                     KSdpAttrIdBasePrimaryLanguage + 
                                                         KSdpAttrIdOffsetServiceName, 
                                     KServiceName);
 
       // Add a description to the record,描述
       iSdpDatabase.UpdateAttributeL(iRecord, 
                                     KSdpAttrIdBasePrimaryLanguage + 
                                                         KSdpAttrIdOffsetServiceDescription, 
                                     KServiceName);
       iSdpDatabase.Close();
       sdp.Close();
       return 0;
}
// 这里设置蓝牙SDP服务中的协议相关信息
void CBtSvr::BuildProtocolDescriptionL(CSdpAttrValueDES* aProtocolDescriptor, TInt aPort)
{
    TBuf8<1> channel;
    channel.Append((TChar)aPort);
 
    aProtocolDescriptor
    ->StartListL()
        ->BuildDESL()
        ->StartListL()   // Details of lowest level protocol
                     // L2CAP层之上
            ->BuildUUIDL(KL2CAP)
        ->EndListL()
 
        ->BuildDESL()
        ->StartListL()
            ->BuildUUIDL(KRFCOMM)
                     // 这里是绑定的RFCOMM的端口号
            ->BuildUintL(channel)
        ->EndListL()
    ->EndListL();
}
 
int CBtSvr::BuildSerivce(const TDesC& ServiceName, TInt KSerialClassID)
{
       TBTSockAddr add;
       int ret;
 
       _LIT(KRFCOMM, "RFCOMM");
       // connect to server
       if (sockSvr.Connect() != KErrNone) { 
              return -1;
       }
       // Open a socket
       if(listenSock.Open(sockSvr, KRFCOMM) != KErrNone) { // ERR_OPEN
              ret = -1;
              goto ERR_CONN;
       }
       // 得到一个可用的RFCOMM端口号
       listenSock.GetOpt(KRFCOMMGetAvailableServerChannel, KSolBtRFCOMM, channelNum);
       add.SetPort(channelNum);
       if(listenSock.Bind(add) != KErrNone) {
              ret = -1;
              goto ERR_OPEN;
       }
       else {
              // 设置安全信息
              TRAPD(err, (SetSecurityOnChannelL(EFalse, EFalse, ETrue, channelNum)));
              if (err != KErrNone) {
                     ret = -1;
                     goto ERR_OPEN;
              }
              // 注册SDP服务
              TRAP(err, (RegieterBlueToothServerL(ServiceName, KSerialClassID)));
              if (err != KErrNone) {
                     ret = -1;
                     goto ERR_OPEN;
              }
              return 0;
       }
 
ERR_OPEN:
       listenSock.Close();
ERR_CONN:
       sockSvr.Close();
       return ret;
}
整个CBtSvr就完成了,其实看着代码比较多,其实就实现了两个功能。蓝牙服务如果有什么不清楚可以多看看SDK自带的那几个示例代码,还有一些nokia提供的相关文档。 

   现在再派生出一个子类BtDbg,实现send功能class BtDbg : public CBtSvr 
  
    { 
  
public : 
  
       ~BtDbg(); 
  
       static BtDbg* NewLC(); 
  
       static BtDbg* NewL(); 
  
       void ConstructL(); 
  
       int Send(const TDesC8& aDesc); 
  
       friend int BtDbg_printf(const char* format, ...); 
  
private: 
  
       int BindL(void); 
  
}; 
  
  
  
BtDbg* BtDbg::NewLC() 
  
{ 
  
       BtDbg* result = new (ELeave) BtDbg(); 
  
       CleanupStack::PushL( result ); 
  
       result->ConstructL(); 
  
       return result; 
  
} 
  
  
  
BtDbg* BtDbg::NewL() 
  
{ 
  
       BtDbg* result = BtDbg::NewLC(); 
  
       CleanupStack::Pop( result ); 
  
       return result; 
  
} 
  
  
  
void BtDbg::ConstructL() 
  
{ 
  
} 
  
  
  
BtDbg::~BtDbg() 
  
{ 
  
} 
  
  
  
int BtDbg::BindL(void) 
  
{     
  
       _LIT(ServiceName, "Debug"); 
  
       return BuildSerivce(ServiceName, 0x1101); 
  
} 
  
  
  
int BtDbg::Send(const TDesC8& aDesc) 
  
{     
  
       if (iStat != EInConnection) { 
  
              return -1; 
  
       } 
  
       // 同步的发送数据 
  
       connSock.Write(aDesc, iSendStatus); 
  
       User::WaitForRequest(iSendStatus); 
  
       if(iSendStatus != KErrNone) { 
  
              return -1; 
  
       } 
  
       return 0; 
  
} 
  
由于比较习惯c的printf来打印调试信息,所以我一般实例化一个BtDbg类,然后再赋给一个全局的指针,写exe程序用全局的东西比较方便。最后封装一个BtDbg_printf来打印trace。 
  
下面代码中的btDbg是事先实例化好的一个BtDbg对象。 
  
static char rxBuf[1024 * 10]; 
  
  
  
int BtDbg_printf(const char* format, ...) 
  
{ 
  
       va_list ap; 
  
       int len = 0; 
  
  
  
       va_start(ap, format); 
  
       vsprintf((char*)rxBuf, format, ap); 
  
       va_end(ap); 
  
       TPtrC8 pBuf = TPtrC8::TPtrC8((unsigned char*)rxBuf); 
  
       if (btDbg && btDbg->Status() == CBtSvr::EInConnection) { 
  
             len = btDbg->Send(pBuf); 
  
       } 
  
       return len; 
  
}