Winsock Basics

Now that we have covered the basics of TCP/IP, let's look at actual Winsock APIs that your application will use to communicate over the network with. To use the Winsock functions in your applications, make sure you include winsock.h in your source, and that you link with the winsock.lib library.

Initializing and Cleaning Up

The first thing to do in order to use Winsock is to make sure that the correct version of winsock.dll is loaded into memory. Even though Pocket PC currently supports only a subset of Winsock version 1.1, it still needs to be initialized in the same way as its desktop counterpart before it is used. This is done by calling the WSAStartup() function, which is defined as follows:

int WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData);

The wVersionRequired parameter specifies which version of Winsock you want to load. For Pocket PC, this value needs to be version 1.1, which can be created by using the MAKEWORD(1,1) macro. The lpWSAData parameter is a pointer to a WSAData structure that WSAStartup() will fill in with information about the version of Winsock that is loaded:

typedef struct WSAData {
   WORD     wVersion;
   WORD     wHighVersion;
   char     szDescription[WSADESCRIPTION_LEN+1];
   char     szSystemStatus[WSASYS_STATUS_LEN+1];
   unsigned short iMaxSockets;
   unsigned short iMaxUdpDg;
   char FAR *lpVendorInfo;
} WSADATA;

Both the wVersion and wHighVersion parameters will return the current version of Winsock that is actually loaded into memory, which will be 0x0101, as only 1.1 is supported. The szDescription and szSystemStatus parameters are not used on Pocket PC and are NULL. The iMaxSockets parameter indicates the recommended maximum number of sockets that an application can actually open. There is no guarantee that your application will be able to open this many sockets. The iMaxUdpDg parameter specifies the largest size of a UDP datagram packet. If this value is 0, there is no limit in this Winsock version. Finally, the lpVendorInfo parameter has a pointer to optional vendor-specific information.

WSAStartup() will return 0 if it succeeds; otherwise, it will return an error code.

Before your application closes, you should call the function WSACleanup(), which is defined as follows:

int WSACleanup(void);

However, note that this function doesn't actually do anything, and is there only to maintain compatibility with desktop applications that have been ported to Windows CE.

Winsock Errors

If an error occurs when calling a Winsock function (except WSAStartup(), which will return an error code), most functions will return the standard Winsock SOCKET_ERROR (defined in winsock.h as -1).

To obtain more information about the error that occurred, you can call the function WSAGetLastError() to find out why the function failed:

int WSAGetLastError (void);

The return value will be the error code for the last network error that occurred. If you need to set the last error for any reason (or to set it to 0), you can use the function WSASetLastError():

void WSASetLastError (int iError);

The parameter iError specifies the new error code.

You can find definitions for the individual Winsock error codes in the winsock.h header file.

Differences between Windows and Pocket PC Winsock Implementations

Besides the fact that Pocket PC supports only a subset of Winsock version 1.1, note a few other minor differences compared to the desktop implementation:

  • There are no asynchronous socket calls. There is no support in Pocket PC for desktop function calls such as WSAAsyncSelect() for notification of socket events.

  • There are no service name APIs. The desktop functions getservbyname() and getservbyport() are not supported on Pocket PC.

  • There are no protocol name APIs. The desktop functions getprotobyname() and getprotobynumber() are not supported on Pocket PC.

  • Unicode and ASCII. Although data that you send over TCP (and UDP messages) can be Unicode or ASCII (Winsock doesn't care, as long as corresponding support exists on both ends and everything is handled properly), most of the Winsock support functions support ASCII parameters only. You need to convert the Unicode strings to ASCII to use them.

  • Blocking versus nonblocking. Sockets are in blocking mode by default. You can set a socket to nonblocking mode by setting the correct socket option, as described in the section "Socket Options."

  • Several socket options are not supported. IP_MULTICAST_LOOP, SO_ACCEPTCONN, SO_RCVLOWAT, SO_SNDLOWAT, SO_SNDTIMEO, and the SO_TYPE socket options are not supported on Pocket PC.

  • IrDA supports only TCP stream sockets. Infrared communication via Winsock (covered in Chapter 5) supports using TCP sockets only.

  • Applications can drop UDP packets. The internal UDP queue buffer size is set to 2. This is a known bug and is documented in Microsoft KB article Q290206.

  • No raw socket support. There currently is no way to create a raw socket. However, you can use the ICMP support functions described in the section "Internet Control Message Protocol (ICMP)" to send ping data.

Finally, note that Windows CE.NET (Windows CE v4.x) now supports Winsock v2.0.

TCP/IP, ActiveSync, and Pocket PC Emulation Issues

While TCP/IP communications are a great way of enabling a Pocket PC device to network with a desktop, a server, or another Pocket PC device, accessing incoming TCP/IP connections can sometimes be a bit tricky if you are not using a network card or a wireless connection. This is typically true during development stages, when devices are often cradled to a desktop and you use the emulation environment.

Neither the emulator nor the device should have any problems establishing connections to a TCP/IP server (in essence, when the device/emulation is acting as a client or initiating a connection), but external resources will not be able to find the device when the device/emulation itself is waiting for incoming connections (i.e., it's acting as a server).

These problems are caused by the way IP addresses are assigned. The emulator uses a private IP address, and ActiveSync uses a Network Address Table (NAT) for cradled communications. Both render it unreachable from the outside world. You can, however, use the "localhost" address on the device itself to establish a connection within the device. For example, if you are writing an application to respond to HTTP requests, and the application is running in the emulation environment, you could use Pocket Internet Explorer inside the emulator to go to http://localhost to access your server.

This should not affect your device when using a dial-up connection, a wireless connection, or a network card.