Developing WinSock Applications for a Mobile Environment

by Lester Memmott


Introduction

This whitepaper is a look at the software developer’s view of network programming and the implications of network failure when using the Windows* WinSock API. It analyzes the use of both TCP and UDP protocols, details the results when a network connection is lost, and provides recommended solutions. The target audience for this information is the software development community writing client-server applications that communicate directly over a network in a mobile environment. This can also be a mixed environment where systems move between Ethernet & 802.11.

The scope of this whitepaper is to:

  • Examine network failure cases when using the Windows WinSock API (TCP & UDP).
  • Provide an explanation of the behavior observed.
  • Provide recommendations for software developers to avoid the pitfalls inherent with network programming in a mobile environment.

 

With the recent ramp of the sale of mobile computers, such as laptops with Intel® Centrino® mobile technology, more and more people are using laptops instead of desktops. Individuals enjoy the freedom of doing work and having fun anywhere and anytime. With this added freedom a new challenge surfaces: intermittent network connectivity. For desktop systems, networks were considered to be highly reliable, but with the introduction of mobility and wireless networks, network connections are much more irregular. Even with this new challenge, users are demanding new features and capabilities and, as always, a robust computing experience.

This whitepaper gives an overview of the standard steps taken by a network-based application using the Windows WinSock API. It then details common coding pitfalls by example through the use of a sample client-server application. Finally, recommendations are provided to implement more robust network applications.


Definitions

TCP: Transmission Control Protocol. Network protocol that is streaming, guaranteed in-order and connection oriented.

UDP: User Datagram Protocol. Network protocol that is datagram based, out of order and connectionless.

802.11: IEEE specification for an industry standard for wireless networking.

Mobile Devices: For purposes of this whitepaper, mobile devices focus on laptops with wireless and/or wired network capabilities (ex. 802.11, Ethernet)

API: Application Programming Interface

 


Standard WinSock Application Flow

In a "Hello-World" (i.e. bare-bones) application written using the WinSock API and the UDP protocol, there are number of steps used to get up and running, get connected, send & receive data and, finally, shut down. The steps are outlined in the following section of pseudo code:

//UDP Example

#include "winsock2.h"

WSAStartup()

gethostbyname() //Get IP address from

//host name

socket(UDP)

while ("work to do")

{

  sendto()

  recvfrom()

}

closesocket()

WSACleanup()

 

Figure 1. UDP Example For TCP based communications a similar flow of steps is used. Steps are outlined in the following section of pseudo-code:

//TCP Example

#include "winsock2.h"

WSAStartup()

gethostbyname()

socket(TCP)

connect()

while ("work to do")

{

  send()

  recv()

}

closesocket()

WSACleanup()

 

Figure 2: TCP Example

Finally, for UDP-based communications an alternate set of steps can be used which more closely follow the TCP example. Steps are outlined in the following section of pseudo-code:

//UDP Case (TCP-like)

#include "winsock2.h"

WSAStartup()

gethostbyname() //Get IP address from host name

socket(UDP)

connect()  /* Allows use of send(), connect()

              instead of recvfrom(), sendto() */

while ("work to do")

{

  send()

  recv()

}

closesocket()

WSACleanup()

 

Figure 3: UDP case using TCP-like coding method

The network can be disrupted anywhere in the sequence of calls listed above. The challenge is to robustly handle any failures that might occur providing the user with a robust experience.


Sample Client-Server Applications

At the end of this whitepaper is a link for downloading two applications that work using a client-server model. The server component listens on a specific port using TCP or UDP protocols. The client component connects to the server, sends a brief message, receives the same message back from the server, and terminates. These two applications implement the algorithms described above which will allow the reader to repeat the connectivity tests described below.


Network Failures - by Example

In a mobile environment a network connection can be lost at any point in this process. It seems reasonable that if the network connection is lost, at some time following the failure, one of the API calls will return an error value. Reason stands (and standard defensive programming dictates) that each WinSock function return value should be checked by the calling function.

Through experimentation, the response of the WinSock API from Figure 1 was tested by disconnecting the network from a system running the code described above. The following behavior was observed:

Connection Failure Point API Failure Point
Before WSAStartup() gethostbyname() – error returned
Before socket() sendto() – error returned
Before sendto() recvfrom() - Hang!
Before recvfrom() sendto() – error returned
After recvfrom() recvfrom() – Hang!

 

Figure 4: Results of UDP tests

The source code from figure 3 was also used (using the UDP protocol) on the same set of tests and the results were equivalent. The surprise here is that instead of returning an error value, recvfrom() simply hangs! The last thing a user wants is for an application to simply hang when the network goes down. Instead, the user wants to save the current work in progress to the local system and continue working or having fun (as the case may be).

The same set of tests were run against the TCP-based code described in figure 2 and the results were as follows:

Connection Failure Point API Failure Point
Before WSAStartup() gethostbyname() – error returned
Before socket() connect() – error returned
Before connect() connect() – error returned
Before send() send() – error returned
Before recv() send() – error returned
After recv() recv() – error returned

 

Figure 5: Results of TCP tests

Unlike the UDP case, TCP tests showed that none of the API calls caused a hang condition. Instead they always returned the appropriate error value.

The key take-aways from this exercise are:

  • Always check the return value of the WinSock APIs.
  • For UDP communications, certain conditions exist that will cause an application to hang. In support of a robust computing experience, users don’t want their applications to hang when disconnected from a network. They want to continue to use their system as if they were connected. Ideally, they want to save any work in process to the local system so that no data is lost.

 


Networking Theory - Connected vs. Connectionless

One fundamental difference between TCP and UDP networking protocols is that TCP is connection-based while UDP is not. Behind the scenes a TCP connection is maintained even when no application data is being transferred through th e use of "Keep-Alive" network packets. These packets are the request-respond mechanism to ensure that the TCP connection is still valid and that both parties are able to communicate with each other. As soon as the network connection goes down, the Keep-Alive requests are no longer received by the second system and, subsequently, the replies stop as well which terminates the connection on both ends.

In the case of the UDP protocol no Keep-Alive packets are sent between the two systems, hence it is called "connectionless". In the UDP test described above, it was found that the recvfrom() call hung. Effectively it was waiting for the network driver to pass back data received off of the network. Because there was no indication that the other system was not available, it patiently waited for a response. Interestingly enough, for these tests even though the network interface was no longer connected to the network, the recvfrom() reported no errors. This behavior is not surprising since Windows-based systems, as well as many other operating systems, will have the loop back interface (i.e. 127.0.0.1) available even when no other networking interfaces exist.


Coding Recommendations

As mentioned earlier, the return values of all WinSock API calls should be checked for any error conditions. As part of defensive programming this is a must and can’t be overlooked. The standard way of doing this is to check for a return value of SOCKET_ERROR, and if returned, call WSAGetLastError() to find out the specific error generated. Note that winsock2.h contains all the valid error return values. This leaves us with the second challenge which is to avoid having an application hang, in the case of UDP, when the network connection goes down. Fortunately, there is a way around this.

The WinSock API supplies a function called setsockopt() which allows the application to set the current value for a socket option associated with a socket of any type, in any state. One of the options associated with a socket is a receive-timeout value by using the SO_RCVTIMEO as the 3rd parameter in the call. Algorithmically the calling application creates a socket, sets the socket to have a timeout value, calls the WinSock API and upon failure calls WSAGetLastError() checking for a return value of WSAETIMEDOUT. In pseudo code this would be:

//UDP Example w/timeout

#include "winsock2.h"

WSAStartup()

gethostbyname() //Get IP address from host name

socket()

int t = 10000; //10 sec

setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char *) &t, sizeof(t))

while ("work to do")

{

  sendto()

  rv = recvfrom()

  if (rv == SOCKET_ERROR) {

    if (WSAETIMEDOUT == WSAGetLastError())

      //recvfrom has timed out

  }

}

closesocket()

WSACleanup()

 

Figure 6: UDP Example with timeout option set

By setting the SO_RCVTIMEO option the recvfrom() call will no longer hang indefinitely, however care must be taken to handle the timeout case. Also, note that SO_SNDTIMEO is another socket option that applies to call to the sendto() function.

Because the timeout case can exist under normal operation due to network latency from a slow link, it is recommended that the timeout value be selected carefully by not making it too short. Also, when a timeout occurs, retries are recommended. In the case when retries also fail to get the data needed, it is recommended that the application switch into an offline mode in which it still operates and responds to user requests, but the information provided to the user would be information cached previously on the local system.


Conclusion

Using the WinSock API in a mobile environment offers a number of challenges that software developers must address. The fundamentals of checking the return value of WinSock API calls should be second nature to most, but also important is to understand that a mobile environment can cause an application to hang when using the UDP protocol. Using socket options (i.e. setsockopt()) the WinSock function calls will no longer block indefinitely. Once set to have a timeout value, the developer must handle this new error case and make the application perform appropriately.


References and Additional Resources

 

Articles

Developer Centers

Community

 

Other Resources

 


Categorie:
Per informazioni complete sulle ottimizzazioni del compilatore, consultare l'Avviso sull'ottimizzazione