Symbian OS Communications (779884), страница 40
Текст из файла (страница 40)
On successful completion,the IP address will be stored in the iAddr member of the name entry:TInetAddr addr = iEntry.iAddr;If there are multiple addresses or aliases, they can be retrieved usingNext():iHr.Next(iEntry, iStatus);until the error KErrEof is returned to indicate that there are no morevalid addresses. Note that it is also possible to receive errors here if, forexample, the network connection is terminated while a query is ongoing,or the network connection has not started. Specific DNS error codesinclude:• −5120, indicating the DNS server could not be contacted. This isoften because the connection has not started, but can also be becausethe configured DNS server address is wrong.USING THE NETWORK CONNECTION179• −5121, indicating that the host is not found in the DNS records.
Thisnormally indicates that the user has mistyped the hostname.TNameEntry contains more than just an IP address – it also containsflags in the iFlags variable indicating the origin of the response. Thesecan be one or more of the following:EDnsAliasAnswer is an aliasEDnsAuthoritativeAnswer is authoritativeEDnsHostsFileAnswer is from host’s fileEDnsServerAnswer is from a DNS serverEDnsHostNameAnswer is host name for this hostEDnsCacheAnswer is from the resolver cacheThe first response to GetByName() is the matching IP address (A(IPv4)or AAAA(IPv6) records, depending on what the DNS server returned).Subsequent calls to Next() will return any further aliases (CNAMEs) orIP addresses (A or AAAA records).
Aliases will have the IP address part ofthe TNameEntry set to 0 and the EDnsAlias flag set.DNS also enables applications to resolve an IP address into a domainname, using the GetByAddress() method, assuming that the reverselookup (or more precisely the PTR record) is set up correctly – which formany Internet hosts is often not the case:TInetAddr iAddr; // usually members of an active objectTNameEntry iEntry;...const TUint32 KInetAddr = INET_ADDR(192,168,1,1);iAddr.SetAddress(KInetAddr);iHr.GetByAddress(iSockServ, iAddr, iEntry, iStatus);SetActive();On successful completion, the name will be stored as a descriptor inthe iName of the TNameEntry:THostName name = entry.iName;As for GetByName(), the first response to GetByAddress() willbe the first matching domain name (PTR record). Subsequent calls toNext() will return any further matching domain names, again untilKErrEof is returned.180IP AND RELATED TECHNOLOGIESNote that for all DNS-based RHostResolver queries, Next() canbe called synchronously because all the information is retrieved duringthe initial query and stored in a local buffer.6.4.3 Sending and Receiving DataAt this point your application has an IP address of a peer to which to senddata.
In most cases, the application will be using the TCP or UDP protocols. It is also possible to transmit raw IP and ICMP but these aren’t discussed here, especially as the NetworkControl capability is required toaccess IP and ICMP sockets. Symbian OS’s socket APIs are based on BSDsockets and should be a familiar paradigm to developers used to socketimplementations on other operating systems. Chapter 3 discusses Symbian OS sockets in more detail, so this chapter only discusses how theyare used for IP data. Figure 6.7 shows the flow of data through the IP stack.Whichever IP-based protocol is used, each RSocket should be boundto the RConnection over which it is intended to be used.TCPFigure 6.6 is a simplified version of the TCP state machine, showing thestates in TCP that are triggered using methods on the RSocket API.Chapter 3 has example code for creating and connecting a TCP socketfor both incoming and outgoing connections. Once the TCP socket isconnected, data transfer is possible.ClosedTime waitClosingListeningConnectingReceivingConnectedSendingFigure 6.6TCP state machineUSING THE NETWORK CONNECTION181ApplicationApplication processComms processESock main threadESockIP thread QTCPUDPQKey:threadboundaryprocessboundaryIPNIFFigure 6.7 Networking subsystem data handlingTCP data transferFor TCP, a call to Write() or Send() will complete once the datahas been queued by the TCP/IP stack.
The application can be notified ofwhen the data is actually sent from the device using the KIoctlTcpNotifyDataSent socket Ioctl (again, Chapter 3 discusses Ioctls in moredetail):sock.Ioctl(KSolInetTcp, KIoctlTcpNotifyDataSent, stat);However, as we discussed in Chapter 3, knowledge of when datahas left a device is less useful that it appears. There is no guaranteethat data is going to make it across the network without one or moreretransmissions, and even then there is no guarantee that even though theremote device has received the data that it has been passed to the remoteapplication.
As a result of this, we recommend that any protocol thatrequires knowledge of whether a particular operation succeeded or failedon a remote host include some form of application level response codesystem – as is used in HTTP, for example, with response codes being sentfor every request.To receive data, use either Recv(), Read(), RecvOneOrMore()or RecvOneOrMore(). These complete at different times with differentamounts of data, as described in Chapter 3, so the application will needto determine which to use depending on its needs.182IP AND RELATED TECHNOLOGIESTerminating the TCP connectionTo terminate the TCP connection, either Shutdown() or Close()needs to be used.
Shutdown() is asynchronous, and performs a cleanshutdown of the socket connection when called with ENormal, sothis is the best method to avoid blocking the thread while the TCPconnection is terminated. However, there are a number of other ways toterminate the connection by passing different parameters to Shutdown()as described in Chapter 3. Close() will shutdown the connection andalso simultaneously close the socket handle, and is a synchronous call. Bydefault, if Close() is called without first calling Shutdown() it behaveslike the ENormal variation of Shutdown(), unless additional data isreceived from the remote device after Close() has been called in whichcase it will behave like an EImmediate Shutdown. This behavior canbe modified by using the KSoTcpLinger socket option with the lingeroption turned on. This can ensure that Close() returns immediately, butwith the TCP disconnection continuing in the background.TSoTcpLingerOpt lingerOpt;lingerOpt.iOnOff = 0; // Offsock.SetOpt(KSolInetTcp, KSoTcpLinger, lingerOpt);Setting the linger option on causes Close() to block until the lingertimeout specified has expired.
After that time, it behaves as the default,and the disconnection will continue in the background.lingerOpt.iOnOff = 1; // OnlingerOpt.iLinger = 2; // 2 secondssock.SetOpt(KSolInetTcp , KSoTcpLinger, lingerOpt);Reconnecting to the same port on the same remote device aftershutdown may fail while the TCP connection is in the WAIT STATE. It ispossible to use the KSoTcpAsync2MslWait socket option to wait for aconnection to become available again.Controlling the behavior of TCP operationsThere are several other socket options and Ioctls supported for TCP, themost commonly used or useful of which are outlined below:Enable TCP keep alive:sock.Ioctl(KSolInetTcp, KSoTcpKeepAlive, 1, iStatus);The following options, are available through RSocket::GetOpt():• KSoTcpSendBytesPending – to discover the number of bytes inthe TCP send queue.USING THE NETWORK CONNECTION183• KSoTcpReadBytesPending – to discover the number of bytes inthe TCP receive queue.UDPUDP is a connectionless datagram-based protocol and so doesn’t involvea connection establishment to the peer device.
The application just needsto specify the local and remote port numbers and IP address to send thedata to. There is no state machine as such, certainly not of the complexityof the TCP state machine.To open a UDP socket, the following parameters must be specifiedwhen the socket is opened:• the Internet address family, KAfInet• the socket type, KSockDatagram• the UDP protocol family, KProtocolInetUdpRSocket sock;err = sock.Open(iSockServ, KAfInet, KSockDatagram, KProtocolInetUdp,iConn);If a known port is to be used for receiving UDP packets, a local portneeds to be bound to the socket. This can also be done dynamically if afixed port isn’t required (and the application can communicate this portnumber to the remote device via a higher layer protocol).
A dynamicallyset port number can be retrieved using the LocalAddr() method. Thisis the same method as for TCP and errors should be handled in the sameway in case the port is already being used by other applications.addr.SetPort(8080);err = iSock.Bind(addr);As with TCP, if the RConnection is not specified when the socketis opened, the implicit connection will be used.
However, because UDPis a connectionless protocol there is no IP traffic sent during connectionsetup so the connection will not be automatically started, unless it hasbeen triggered by another means, for example, a DNS lookup. If theapplication is expecting to receive UDP packets before sending any, itwill need to explicitly start the network connection to ensure it is active.This is important, as no data will be received if the network connectionisn’t active! This can be done using the RConnection API as describedearlier in the chapter, or alternatively the implicit connection will bestarted when either Connect() is called on a UDP socket or if data iswritten to the socket.