Symbian OS Communications (779884), страница 32
Текст из файла (страница 32)
There’s something going wrong...User::Leave(KErrCorrupt);break;}}else{// Service discovery failed.User::Leave(err);}}Example 5.2Service discovery code, integer typeIRDA IN SYMBIAN OS139In this next case, we’re going to demonstrate retrieving the remotedevice name, by querying for the general ‘Device’ class, with an attributeof ‘DeviceName’. These are standard names and attributes from the IrDAspecification that allow us to retrieve the remote device’s nickname.We’ll use the same class names in Example 5.3 as we did in Example 5.2,but obviously these implement different functionality, so you’d need tocombine the two in some way if you want both functions!class CIrServiceDiscoverer : public CActive{public:...void DiscoverRemoteDeviceNameL();virtual void RunL();// DoCancel would also need implementing for real applications...private:RSocketServ iSs;RNetDatabase iServices;TIASQuery iQuery;TIASResponse iResponse;TBuf16<KMaxQueryStringLength> iDeviceName;TIrdaSockAddr iDiscoveredDevice; // assume already discovered and// address stored in here};_LIT8(KClassDevice, "Device");_LIT8(KAttributeDeviceName, "DeviceName");void CIrServiceDiscoverer::DiscoverRemoteDeviceNameL(){// Open a connection to socket serverUser::LeaveIfError(iSs.Open());// Open an IrDA service resolverUser::LeaveIfError(iServices.Open(iSs, KIrdaAddrFamily, KIrTinyTP));// Run a service inquiry sequencequery.Set(KClassDevice, KAttributeDeviceName,iDiscoveredDevice.GetRemoteDevAddr);iServices.Query(query, response, iStatus);SetActive();}void CIrServiceDiscoverer::RunL(){if (iStatus.Int() == KErrNone){switch (response.Type()){case EIASDataMissing:User::Leave(KErrNotFound);break;140INFRAREDcase EIASDataUserString:{// Save the string returnedTBuf8<KMaxQueryStringLength> rawBytes;TInt charSet = result.GetUserString(rawBytes);if (charSet < 0){User::Leave(charSet);}ConvertCharacterSetL(charSet, rawBytes, iDeviceName));break;}case EIASDataInteger:case EIASDataOctetSequence:default:// We asked for a device name, and received something other// than a string.
There’s something going wrong...User::Leave(KErrCorrupt);break;}}else{// Service discovery failed.User::Leave(iStatus.Int());}}Example 5.3Service discovery code, user string typeIn this example, the conversion from raw bytes in the supplied characterset to Unicode half-words has been left to a function ConvertCharacterSetL, which would use most of the code from Example 5.1, butobviously without the buffers that were declared in that example, as thisfunction supplies them.5.3.6 Service RegistrationService registration is performed via TIASDatabaseEntry.NONSHARABLE_CLASS(TIASDatabaseEntry) :public TPckgBuf<TIASDatabaseEntryV001>{public:IMPORT_C void SetClassName(const TDesC8& aClassName);IMPORT_C void SetAttributeName(const TDesC8& aAttributeName);IMPORT_CIMPORT_CIMPORT_CIMPORT_C};voidvoidvoidvoidSetToInteger(const TUint aInteger);SetToCharString(const TDesC8& aCharString);SetToCharString(const TDesC16& aWideStringSetToOctetSeq(const TDesC8& aData);IRDA IN SYMBIAN OS141SetClassName() and SetAttributeName() set the entries classand attribute fields respectively, whilst the remaining methods set the datacontained within the entry.
Of the four accessors, the three descriptorrelated methods have caveats attached, due to descriptor length limitations. As mentioned before, Symbian OS limits the size of octet sequencesand user strings to 125 bytes. Calling the setter methods with a largerbuffer will result in a descriptor overflow panic, unless the buffer size isgreater than 256 bytes. If the buffer is this large, the stack will raise panicIrda Panic-1 instead.Another point to note is the character-encoding scheme used by thetwo SetToCharString() methods.
These methods set user strings,using either ASCII or UCS-2 encoding. There is no way to specify adifferent encoding method.Once the TIASDatabaseEntry object has been populated, it maythen be passed to RNetDatabase:: Add() to be stored in the IASdatabase.class CIrRegisterLocalService : public CActive{public:...void RegisterServiceL();virtual void RunL();...private:RSocketServ iSs;RNetDatabase iServices;TIASDatabaseEntry iRecord;Tint iObexPort; // should get this when opening listening socket and// calling AutoBind(), ie. socket must be open before// we attempt to register the service!};_LIT(KClassDefaultObexService, "OBEX");_LIT(KAttributeLsapSel, "IrDA:TinyTP:LsapSel");void CIrRegisterLocalService::RegisterServiceL(){User::LeaveIfError(ss.Open());// Open an IrDA service resolverUser::LeaveIfError(services.Open(ss, KIrdaAddrFamily, KIrTinyTP));// Build an IAS recordrecord.SetClassName(KClassDefaltObexService);record.SetAttributeName(KAttributeLsapSel);record.SetToInteger(iObexPort);// Insert record into databaseservices.Add(record, iStatus);SetActive();}142INFRAREDvoid CIrRegisterLocalService::RunL(){User::LeaveIfError(status.Int()); // service failed to register}Example 5.4Inserting a record in the local IAS databaseOne possible cause of errors with this code is re-registering an entrythat already exists.
In this case the IAS server will detect the conflict andreturn a KErrAlreadyExists value.It is also worth noting that closing the RNetDatabase handle doesnot remove any entries registered through this handle – see the section‘5.3.7 Service removal’ for details of how to deregister a service.Hint bitsIn addition to the IAS databse, IrDA also provides a small set of devicehint bits, which are returned at the discovery stage.
This allows a simplefiltering operation to be performed before deciding to connect to adevice.The Symbian API to retrieve or set these bits is through socket optionson an open RSocket instance. In this example, we’ll enable the hint bitthat indicates that we provide OBEX services.// iSocket is an already opened socket that will be used for the service// we are providingvoid SetHintBitL(){// Retrieve the current hint byte (OBEX is in the second byte)TDes8<sizeof(TUint)> buffer;User::LeaveIfError(iSocket.GetOpt(KSecondHintByteOpt, KLevelIrLap,buffer));TUint8 secondByte = buffer[0];// Force on the OBEX bitsecondByte | = KIrObexMask;buffer[0] = secondByte;// Set new hint byteUser::LeaveIfError(iSocket.SetOpt(KSecondHintByteOpt, KLevelIrLap,buffer));}// Run an OBEX server...// OBEX server terminatedvoid ClearHintBitL(){// Retrieve the current hint byteUser::LeaveIfError(socket.GetOpt(KSecondHintByteOpt, KLevelIrLap,buffer));IRDA IN SYMBIAN OS143TUint8 secondByte = buffer[0];// Force the OBEX bit offsecondByte &= ∼KIrObexMask;buffer[0] = secondByte;// Set the new hint byte (clearing the bit)User::LeaveIfError(socket.SetOpt(KSecondHintByteOpt, KLevelIrLap,buffer));}Example 5.5Setting and clearing hint bits on the local deviceThis example shows both setting and clearing a hint bit and showsan important aspect of the API – the stack does not manage hint bits forthe programmer.
As a result, setting a hint bit without first getting itscurrent value may lose hint bits set by other applications. This does leadto potential race conditions with two applications trying to add a newhint bit, where both applications could read the current hint bits, OR intheir new hint bit, then write the new value to the stack. In that case,unless they were both setting the same bit, one of the writes would belost.However, the biggest problem is seen when clearing hint bits, as thereis no way to be sure that another application is not providing a service thatrequires the same hint bit to be set as your application. The consequenceof this is that we recommend that you do not ever clear hint bits – thatway you do not risk breaking other application’s use of them.
Since theyare hint bits, indicating roughly which services a device supports, thisshould not result in any major problems – other devices may connectunnecessarily because they believe the local device is offering a servicewhich it is not, but this can happen in normal operation, as one hint bitcovers a series of possible services.5.3.7 Service RemovalWhen removing an IAS record, you need a TIASDatabaseEntry withthe same details as the one you used to register your service. This can bethe same instance you used to insert the record into the database, if youkept it around, or it can be an entirely new object created using the sameparameters.Once the class and attribute names have been set, the object is passedto RNetDatabase::Remove(), and the entry will be removed fromthe database or an error raised if the record cannot be found.
There is noneed to set the attribute value.It is also worth noting that the RNetDatabase object may be acompletely unrelated connection to the one which originally registeredthe entry – it may even be in a different process.144INFRAREDclass CIrDeregisterLocalService : public CActive{public:...void DeregisterServiceL();virtual void RunL();...private:RSocketServ iSs;RNetDatabase iServices;TIASDatabaseEntry iRecord;};_LIT(KClassDefaultObexService, "OBEX");_LIT(KAttributeLsapSel, "IrDA:TinyTP:LsapSel");void CIrDeregisterLocalService::DeregisterServiceL(){User::LeaveIfError(iSs.Open());// Open an IrDA service resolverUser::LeaveIfError(iServices.Open(ss, KIrdaAddrFamily, KIrTinyTP));// Build an IAS recordiRecord.SetClassName(KClassDefaultObexService);iRecord.SetAttributeName(KAttributeLsapSel);// Remove record from databaseiServices.Remove(iRecord, iStatus);SetActive();}void CIrDeregisterLocalService::RunL(){User::LeaveIfError(status.Int()); // service was not deregistered}Example 5.6Deregistering a service from the local IAS database5.3.8 Client SocketsNow that we’ve seen how to query for devices, register local service and query for remote services, and set the appropriate hint bits,it’s time to look at actual data transfer.
First we’ll consider outboundconnections in this section, then incoming connections in ‘Server sockets’.Connecting a client socket to a server running on a remote machine isquite simple. First we need to open the socket. The RSocket::Open()call takes a number of parameters – protocol family, socket type andprotocol identifier. Constants are provided for all of these, but in the caseof IrDA, the most important two are the protocol family (KIrdaAddrFamily), and the socket type (defined in es_sock.h). It is still worthusing the appropriate protocol identifier, however.IRDA IN SYMBIAN OS145Table 5.3 summarizes the correct parameters for the call to Open:Table 5.3 Call parameters for OpenAddress familySocket typeProtocol IDIrMUXKIrdaAddrFamilyKSockDatagramKIrmuxTinyTPKIrdaAddrFamilyKSockSeqPacketKIrTinyTPNext, the remote address and port are found, using the techniquesfor device and service discovery that we covered earlier in the chapter,and passed to the stack by means of a call Connect().