Wiley.Developing.Software.for.Symbian.OS.2nd.Edition.Dec.2007 (779887), страница 65
Текст из файла (страница 65)
The example retrieves this data and prints it to thescreen, using the following lines of code:while(1){// Wait to receive, nRet = NumberOfBytesReceivednRet = recv(sock, buffer, sizeof(buffer), 0);if (nRet <= 0)break;puts(buffer);}334SYMBIAN OS TCP/IP NETWORK PROGRAMMINGrecv() is used for TCP sockets (or UDP sockets in which connect()was called) to receive data. The data is then placed in a supplied buffer.recv() returns the number of bytes received (if this is zero it means theconnection was terminated, if negative an error occurred).
It has the form:int recv(int socket, void *buffer, size_t length, int flags);In the case of UDP sockets, you usually use recvfrom(). It’s the sameas recv() except it returns the address of the endpoint that sent thepacket. It has the form:int recvfrom(int s, void *buf, size_t len, int flags,struct sockaddr *from, socklen_t *fromlen);Cleaning up the connectionOnce the web page retrieval is complete, the socket is cleaned up asfollows:close(sock);close(int socket) closes the socket and shuts down the connection.A function called shutdown(), prototyped as int shutdown(int s,int how), also exists to shut down a specific direction of the session.The parameter how has three possible values: SHUT RD disallows furtherreception; SHUT WR disallows further transmission; and SHUT RDWRdisallows both reception and transmission.11.3Symbian OS Socket APISymbian OS provides a C++ socket API that, as previously mentioned, is inmany ways similar to the BSD C-based socket API.
In addition to allowingTCP/IP communication, the Symbian OS socket API allows for other typesof communication as well, including Bluetooth technology, USB, and IR(although I will be covering only TCP/IP in this chapter). Underlyinglayers in the communication architecture handle these communicationdifferences, and the socket API can be used in a transport-independentway.11.3.1 Socket API ClassesFirst, let’s briefly look at the key classes of the Symbian C++ socket APIthat you’ll use for TCP/IP communication:SYMBIAN OS SOCKET API335•RSocketServ is the client-side class for the socket server and mustbe created and connected in order for your program to establish asession with the socket server. There is no equivalent to this class inthe BSD socket API.•RSocket represents a single socket in much the same way as doesthe handle returned by the socket() function in the BSD API.
Theother methods of RSocket correspond, for the most part, with theBSD network API functions.•RHostResolver provides methods for both getting an IP addressfrom a given domain name, and getting a domain name from agiven IP address. DNS is used in the case of TCP/IP. RHostResolver methods GetByName() and GetByAddr() provide thesame functionality as the C API socket functions gethostbyname()and gethostbyaddr(), respectively.RSocketServ is a client session class to the socket server and isderived from RSessionBase. RSocket and RHostResolver are subsessions to an established RSocketServ session and are derived fromRSubSessionBase.11.3.2 HTTP Example Using Symbian OS Socket APINow let’s look at the OutputWebPage() program from section 11.2.3,rewritten to use the Symbian OS socket API.
The first thing to note isthat many of the socket functions are asynchronous functions, and Iuse User::WaitForRequest() to wait for them to complete. Thisis for simplicity in showing the API; however, these functions are mosteffectively used in active objects (see section 11.3.3).Note that Symbian OS does provide an HTTP framework API, andusing this framework makes the most sense when implementing codeto retrieve web pages (I will not cover this framework in this book).However, I will use the general Symbian OS network APIs to implementthe HTTP examples here for the purposes of demonstrating generalnetwork programming in Symbian OS.#include <in_sock.h>void HandleError(TDesC& aMsg, RSocketServ& aSockServ){aSockServ.Close();PrintError(aMsg);}TInt OutputWebPage(const TDesC& aServerName, const TDesC& aDoc){RSocketServ sockSrv;TInt res = sockSrv.Connect();if (res != KErrNone){336SYMBIAN OS TCP/IP NETWORK PROGRAMMING_LIT(KSockOpenFail,"Error connecting to socket server");HandleError(KSockOpenFail,sockSrv);return res;}RSocket sock;res = sock.Open(sockSrv,KAfInet,KSockStream, KProtocolInetTcp);if (res != KErrNone){_LIT(KSockOpenFail,"Socket open failed");HandleError(KSockOpenFail,SockServ);return res;}TNameEntry nameEntry;RHostResolver resolver;res = resolver.Open(sockSrv, KAfInet, KProtocolInetTcp);if (res != KErrNone){_LIT(KResvOpenFail,"host resolver open failed");HandleError(KResvOpenFail, SockServ);return res;}TRequestStatus status;resolver.GetByName(aServerName, nameEntry, status);// wait for completion of asynchronous function.
In real code,// much better to use active objects for completions instead of these// calls.User::WaitForRequest(status);resolver.Close();if (status != KErrNone){_LIT(KDnsFail,"DNS lookup failed");HandleError(KDnsFail,SockServ);return res;}TInetAddr destAddr;destAddr = nameEntry().iAddr; // set address to DNS-returned IP// addressdestAddr.SetPort(80); // Set to well-known HTTP port// Connect to the remote hostsock.Connect(destAddr,status);User::WaitForRequest(status);if (status != KErrNone){_LIT(KSocketConnectFail,"Failed to connect to server");HandleError(KSocketConnectFail,SockServ);return res;}// Assemble HTTP GET commandTBuf8<300> getBuff;_LIT8(KGetCommand,"GET");getBuff.Copy(KGetCommand);getBuff.Append(aDoc);LIT(KCRLF, "\xD\xA");getBuff.Append(KCRLF);// Send HTTP GETsock.Send(getBuff,0,status);User::WaitForRequest(status);TBuf8<200> buff;SYMBIAN OS SOCKET API337do{TSockXfrLength len;sock.RecvOneOrMore(buff,0,status,len);User::WaitForRequest(status);PrintOutput(buff); // some generic 8-bit output-to screen or file} while (status == KErrNone);sock.Close();sockSrv.Close();return (KErrNone);}Connecting to the socket serverBefore using the socket API, you must first establish a session with thesocket server.
Note that when I use the term ‘socket server’ here, I meanthe client–server process in Symbian OS that handles sockets, and nota destination server machine. To establish a session with the socketserver, you instantiate an RSocketServ object and call its Connect()function, as shown below:RSocketSrv sockSrv;TInt res = sockSrv.Connect();if (res != KErrNone){_LIT(KSockOpenFail,"Error connecting to socket server");HandleError(KSockOpenFail,sockSrv);return res;}Socket handling, like many other functions in Symbian OS, is bestperformed by means of a server process, along with client-side interfaceclasses to access the server’s services.
The client-side classes for thesocket server comprise the socket API.The socket server handles all the details of creating sockets, connecting them to the client and server, and communicating through socketsin a transparent fashion. At this level of network programming, youdon’t need to know the details of the Symbian OS network communication architecture but, if you are interested, they are covered in greaterdepth in Chapter 3 (see section 3.10), and in a forthcoming book fromSymbian Press.3Creating the socketTo create and open a socket, you instantiate an RSocket class and callits Open() method. This is done in the example as:TInt res = sock.Open(sockSrv,KAfInet,KSockStream, KProtocolInetTcp);3Symbian OS Communications Programming, John Wiley & Sons, 2007, ISBN0470512288.338SYMBIAN OS TCP/IP NETWORK PROGRAMMINGOpen() has the following form:TInt Open(RSocketServ& aServ,TUint aAddrFamily, TUint aSocketType, TUintaProtocol);The first argument is the connected RSocketServ class – this is neededbecause each RSocket is a subsession of the client socket server sessionestablished by RSocketServ.The last three arguments are similar to those for the C socket() call.KAfInet specifies the TCP/IP v4 protocol suite.
socketType is set toKSockStream for TCP, or KSockDatagram for UDP.protocol should be:•KProtocolInetTcp for TCP•KProtocolInetUdp for UDP.Unlike in the BSD socket API, protocol cannot be zero.Setting the destination addressThe class TInetAddr represents an endpoint’s IP address and port, whichcan be set up using SetAddress() and SetPort(), respectively. Forexample, the following code sets up a TInetAddr to represent IP address10.1.2.3, port 80:TInetAddr addr;addr.SetAddress(INET_ADDR(10,1,2,3));addr.SetPort(80);INET ADDR is a macro that writes the quad address into a 32-bit valuethat contains the four address bytes.In our HTTP example, we are passed the web server name, so weneed to use the RHostResolver class to contact DNS and look upthe IP address associated with that name.
To use the RHostResolver,you open it for the appropriate protocol (in this case TCP) and then callRHostResolver’s GetByName() method to look up the correspondingIP address.In the example, this is accomplished by:TNameEntry nameEntry;RHostResolver resolver;res = resolver.Open(sockSrv, KAfInet, KProtocolInetTcp);if (res != KErrNone){SYMBIAN OS SOCKET API339_LIT(KResvOpenFail,"host resolver open failed");HandleError(KResvOpenFail, SockServ);return res;}TRequestStatus status;resolver.GetByName(aServerName, nameEntry, status);User::WaitForRequest(status);resolver.Close();if (status != KErrNone){_LIT(KDnsFail,"DNS lookup failed");HandleError(KDnsFail,SockServ);return res;}As mentioned previously (see section 11.3.1), GetByName() convertsthe server name in aServerName to an IP address.