Symbian OS Communications (779884), страница 13
Текст из файла (страница 13)
More details onthis subject can be found in Chapter 4.RNetDatabaseRNetDatabase provides service discovery facilities. These are highlyprotocol-specific, so we’ll defer discussion of where and when to use thisto the chapters dealing with each protocol. Do not be surprised if you donot see explicit usage of RNetDatabase in those chapters though, as it isoften concealed behind a set of utility classes – for example, CSdpAgentin the case of Bluetooth.3.2 Into PracticeNow it’s time to roll up our sleeves and try putting some of this theory intopractice. We’ll start with a very simple program to retrieve the Symbianhomepage to show the basics.
Then we’ll build a simple wrapper aroundRSocket that you can use as the basis for your own sockets programming.The focus will be on RSocket as it’s at the heart of accessing resourceson networks. The connection management and resolution services arecovered in more detail in later chapters.As web pages are served using the HTTP protocol, which is transported over TCP/IP, this example will touch on some TCP/IP networkingconcepts. These are more fully explained later in the book and so wewon’t be giving much detail here.
This is an extremely low-level way toaccess the HTTP protocol – the Symbian HTTP framework (described inChapter 11) provides a better method.To get any of these examples to work on the Symbian emulator you’llneed to have configured the emulator to access an internet connection – see Chapter 13 for details on how to do this.3.2.1 The Bare BonesOur simple example program can be found in the esock\simple1directory. If you look in the simple1.cpp file at the getPage()function, you’ll see code to retrieve the page.INTO PRACTICE53First, let’s see the class declaration:class CGetWebPage : public CActive{enum TState{EConnectingSocket,ESendingData,EReceivingData};...public:static CGetWebPage* NewL(); // the usual patternvoid GetPageL();virtual void RunL();// we would also need to implement DoCancel() in real code...private:void ConstructL()private:RSocketServ iSs; // needs Connect()ing, but omitted for clarity in this//exampleRSocket iSock;TInetAddr iWebServAddr;RTest iTest;TBuf8<255> iData;TBuf16<255> iData16; // purely for display purposes using iTestTSockXfrLength iLen;}As you’d expect, the first thing the code does is connect to the socketserver, and open a socket.void CGetWebPage::ConstructL(){// Connect to the socket serverUser::LeaveIfError(iSs.Connect());// Open the socketUser::LeaveIfError(iSock.Open(ss, KAfInet, KSockStream,KProtocolInetTcp));}As we’re using TCP/IP for this example, we specify the TCP/IP protocolin the parameters to RSocket::Open():• KAfInet – the internet protocol family• KSockStream – TCP is a stream protocol• KProtocolInetTcp – the TCP protocol.The same code works for other protocols such as Bluetooth orIrDA – simply change the parameters to the Open() call.54AN INTRODUCTION TO ESOCKNext we will connect to the web server at www.symbian.com.
In areal program we would use the DNS facilities of Symbian OS to look upthe right IP address (see Chapter 6 for details), but here we’ve hard-codedit. Don’t do this in a real program – IP addresses can and do change!We also hardwire the port number as 80, but that’s OK as this is thewell-known port for web servers and so won’t change.void GetPageL(){iState = EConnectingSocket;iAddr.SetAddress(INET_ADDR(81,89,143,203)); // hardwired address of// www.symbian.com – DON’T do this in real codeiAddr.SetPort(80); // HTTP port...}TInetAddr is the TCP/IP-specific address class derived from TSockAddr so can be passed to the RSocket::Connect() call.
Next wemake the connection, then send the HTTP request to the web server.Again, this has been hard-coded here rather than being built up out ofthe individual HTTP request headers._LIT8(KReq, "GET / HTTP/1.0\r\nHost: www.symbian.com\r\n\r\n");void CGetWebPage::GetPageL{...User::LeaveIfError(iSock.Connect(iAddr, iStatus));SetActive();}void CWebPage::RunL(){switch(iState){case EConnectingSocket:iTest.Printf(_L("Connect returned %d\n"), iStatus.Int());iTest.Printf(_L("Sending request...\n"));iState = ESendingData;iSock.Send(KReq, 0, iStatus);SetActive();return;case ESendingData:...}}Provided the send completes successfully, we can now read theresponse from the web server:class CGetWebPage : public CActive{...}INTO PRACTICE55void CWebPage::RunL(){...case ESendingData:iTest.Printf(_L("Request sent\n"));iTest.Printf(_L("Receiving data.
. .\n"));iSock.RecvOneOrMore(iData, 0, iStatus, iLen);iState = EReceivingData;SetActive();return;case ESendingData:iTest.Printf(_L("Recv status %d, len %d\n"), iStatus.Int(),iLen());iData16.Copy(iData);iTest.Printf(iData16);break;...}Notice that we use RecvOneOrMore() to receive the response, as wedon’t know how long it will be. If we had instead used Read() with thesame buffer, if the response was less than 255 bytes the program wouldhave hung waiting for more data from the web server.That’s it – you’ve now seen the simplest program which communicatesover TCP/IP. As you can see, this already does something useful, in thiscase retrieve a web page. This is often the case when creating a clientprogram – relatively little code can result in big effects, since the serveralready exists.
Of course if you’re having to write both the client andserver there’s no free lunch and you’ll have plenty to do!From this simple beginning we’ll now move on to a set of skeletonsocket wrapper classes that deal with most of the housekeeping neededfor socket communications. To keep the example clear, we have madesome design tradeoffs:1. The reading and writing are targeted towards stream sockets.
Thecode will work with datagram sockets, but any datagram boundarieswill be lost.2. There’s no buffering strategy implemented. How to manage thebuffers is left to the caller. Depending on the final application,buffering may be critical to performance. For example, a programwhich is downloading a lot of data to a file will probably want touse a double-buffering strategy to maximize throughput, whereas aprogram which needs low-latency transmission of small amounts ofdata would need to minimize the use of buffers.3.
The classes provide a call-back based API for event notification. Thismeans that internally they provide active objects to interface to theasynchronous calls and then transform the completed events into56AN INTRODUCTION TO ESOCKcallbacks. We have taken this approach to show how to write theactive objects needed to use RSocket.The code for this wrapper can be found in \esock\csocket. It willbe useful to have the code handy while reading this section, so you cantrace through the discussion, however, the most important code snippetsare shown here so don’t worry if you don’t have access to the code rightnow.There are two classes at the heart of this wrapper: CSocket andCSocketFountain.
Let’s start with CSocketFountain. You useCSocketFountain if you want to receive incoming connections. Itprovides a simple way to start listening for connections, and when onecomes in it passes a new CSocket instance back, ready to be used tocommunicate with the remote peer. Hence the name – you can think ofthis class as creating a fountain of new sockets, one per connection. Justlike a fountain there is a catch – there’s no way to rate-limit how manyconnections are created, so it’s possible to get soaked! If your programis going to be running on a network where large numbers of connectionsmight be made, either intentionally or maliciously (i.e., the Internet), thenyou should add some form of rate-limiting on incoming connections anda cap on the maximum number of simultaneous connections to preventdenial of service attacks on the device.CSocket provides methods to make an active connection, and,once connected, methods for sending, receiving and closing down thesocket.Let’s look at how CSocket and CSocketFountain are used:TInetAddr addr(KInetAddrLoop, 7777);// create a listening connectioniSockFountain = CSocketFountain::NewL(*this);iSockFountain->Listen(KAfInet, KSockStream, KProtocolInetTcp, addr);// then create a connection to itiSock = CSocket::NewL();iSock->SetSocketCallback(*this);iSock->Connect(KAfInet, KSockStream, KProtocolInetTcp, addr);First we create a new CSocket, and set the socket to callback theappropriate object (in this case *this).
The callback is via a MCSocketCallbacks interface, so the caller must implement this interface.Next we setup a CSocketFountain, here the callback is through theMIncomingConnection interface which again we must implement.Finally, we tell the socket fountain to listen on a certain address, and thenthe CSocket to connect to the same address. The address we’re using isthe localhost address, so the result is that the CSocket is looped-back toour CSocketFountain. Of course this would also work using a remoteaddress and having two devices both running the program.INTO PRACTICE57When CSocketFountain receives a connection, a new CSocketis created and passed back to the fountain owner, through the MIncomingConnection interface:class MIncomingConnection{public:virtual void IncomingConnection(CSocketFountain&, CSocket& aSock)=0; //// Transfers ownershipvirtual void ListeningError(CSocketFountain& aFountain, TInt aErr)=0;};Both callbacks specify the CSocketFountain, to make it easier tokeep track when using multiple CSocketFountain objects within aclass, e.g., when listening on multiple ports or addresses.That’s it on the receiving connections side – the CSocket passed toIncomingConnection() is ready for use.On the CSocket side, once the connection is made, a callback occursthrough the MCSocketCallbacks class:class MCSocketCallbacks{public:virtual void ConnectComplete(CSocket&, TInt aErr) = 0;virtual void NewData(CSocket&, TDes8& aBuf) =0;virtual void WriteComplete(CSocket&, TInt aErr, TInt aLen) =0;};As with CSocketFountain, the CSocket that is making the callbackis passed as an argument, so that the client can have one callback functionto handle multiple CSockets.