Establishing and Maintaining Connections

Now that you have the proper network identifier to use for the network location from which you are requesting a resource, you can go ahead and tell Connection Manager to make the connection.

The Connection Manager API currently supports two different functions for establishing a connection to the network:

  • Synchronous Mode: To create a connection request that will not return until the connection either is established or returns an error, you can use the ConnMgrEstablishConnectionSync() function.

  • Asynchronous Mode: To create a connection request that returns immediately, you can use the ConnMgrEstablishConnection() function. You need to poll the request handle that you are returned in order to find out the current status of the connection.

Regardless of which method you use to establish your network connection, you need to properly fill out a CONNMGR_CONNECTIONINFO structure, which contains the parameters that describe the request. The structure is defined as follows:

typedef struct _CONNMGR_CONNECTIONINFO {
   DWORD cbSize;
   DWORD dwParams;
   DWORD dwFlags;
   DWORD dwPriority;
   BOOL bExclusive;
   BOOL bDisabled;
   GUID guidDestNet;
   HWND hWnd;
   UINT uMsg;
   LPARAM lParam;
   ULONG ulMaxCost;
   ULONG ulMinRcvBw;
   ULONG ulMaxConnLatency;
} CONNMGR_CONNECTIONINFO;

The cbSize member should be set to the size of the CONNMGR_CONNECTIONINFO structure.

The dwParams member contains a list of optional member fields that are set in the structure, and can be a combination of the values shown in Table 7.1.

The dwFlags member defines the list of flags that specify any special properties for establishing the network connection. If no flags are set, the Connection Manager will use a direct IP connection. The dwFlags member should be set to one of the values shown in Table 7.2.

Table 7.1. CONNMGR_CONNECTIONINFO Flags

Value

Description

CONNMGR_PARAM_GUIDDESTNET

guidDestNet field is valid

CONNMGR_PARAM_MAXCOST

ulMaxCost field is valid

CONNMGR_PARAM_MINRCVBW

ulMinRcvBw field is valid

CONNMGR_PARAM_MAXCONNLATENCY

ulMaxConnLatency field is valid

Table 7.2. Connection Manager Proxy Flags

Value

Description

CONNMGR_FLAG_PROXY_HTTP

HTTP Proxy supported

CONNMGR_FLAG_PROXY_WAP

WAP gateway proxy supported

CONNMGR_FLAG_PROXY_SOCKS4

SOCKS4 proxy supported

CONNMGR_FLAG_PROXY_SOCKS5

SOCKS5 proxy supported

The dwPriority member specifies the priority level of the connection you are requesting. Remember that the Connection Manager needs to delegate various simultaneous network requests among multiple applications. It gives precedence to those that specify a higher priority value. Table 7.3 describes the priority levels that can be used.

The bExclusive member specifies whether the connection can be shared among multiple applications. If you set this value to FALSE, then other programs will be notified when the connection is available. If you set it to TRUE, then you place the connection in a state in which it cannot be shared. Other applications that request to establish a similar connection will fight for the same resource, with the outcome depending on each connection request's priority.

The bDisabled member prevents the connection request from actually establishing a remote connection if set to TRUE. This can be used to test the availability of a network connection.

The guidDestNet member should contain the GUID for the network identifier that will be used to connect to the network. This is the same identifier returned using the ConnMgrMapURL() or ConnMgrEnumDestinations() functions. A remote connection cannot be established without this identifier.

Table 7.3. Connection Manager Priority Levels

Value

Priority

Description

CONNMGR_PRIORITY_VOICE

Highest

Voice connection.

CONNMGR_PRIORITY_USERINTERACTIVE

 

A user has made this request, and is awaiting the creation of the connection.

  

Use this priority for user-interactive applications.

CONNMGR_PRIORITY_USERBACKGROUND

High

The application has become idle. You should switch to this priority when the application is not active.

CONNMGR_PRIORITY_USERIDLE

 

A user-initiated request has been idle for a length of time. Switching between this and CONNMGR_PRIORITY_USERINTERACTIVE enables the Connection Manager to optimize shared connections.

CONNMGR_PRIORITY_HIPRIBKGND

 

High-priority background.

CONNMGR_PRIORITY_IDLEBKGND

Low

Idle background task.

CONNMGR_PRIORITY_EXTERNALINTERACTIVE

 

A network request has been made from an external application.

CONNMGR_PRIORITY_LOWBKGND

Lowest

A connection is established only if a higher-priority client is already using the connection.

CONNMGR_PRIORITY_CACHED

None

Internal caching is being used; no external connection is needed.

To have the Connection Manager send any changes in the connection status to a window handle, you can use the next three members to set that up. The hWnd member is the window handle that you want to receive messages, uMsg should contain the WM_USER message ID that you want to have sent with the status change, and lParam is a DWORD value that will be placed in the lParam parameter of your message. You can set all three of these members to zero if you don't want the Connection Manager to post any messages to your window.

The ulMaxCost member should specify the maximum cost of the connection.

The ulMinRcvBw member specifies the minimum amount of bandwidth that you need in order to accept the connection.

Finally, the ulMaxConnLatency member should specify the maximum acceptable connection latency, in milliseconds, before a connection fails. An acceptable value for the maximum latency would be around 4 seconds, or 4,000 milliseconds.

Establishing Connections

To create a synchronous connection, you can call the following function:

HRESULT WINAPI ConnMgrEstablishConnectionSync(
   CONNMGR_CONNECTIONINFO *pConnInfo, HANDLE *phConnection,
   DWORD dwTimeout, DWORD *pdwStatus);

The pConnInfo parameter should point to a CONNMGR_CONNECTIONINFO structure that contains the instructions for establishing the connection. This is followed by phConnection, a pointer to the connection handle that you are returned from the function. The dwTimeout parameter should be used to set a timeout value, in milliseconds, which the function will return if a connection cannot be established. Finally, the pdwStatus parameter will point to the final status of the connection.

Don't forget that you need to call the ConnMgrReleaseConnection() function once you are finished using the connection in order to properly free the connection handle.

The following code establishes a synchronous connection:

// Establish a synchronous connection
HANDLE hConnection = NULL;
DWORD dwStatus = 0;
DWORD dwTimeout = 5000;

// Get the network information where we want to establish a
// connection
TCHAR tchRemoteUrl[256] = TEXT("\0");
wsprintf(tchRemoteUrl,
  TEXT("http://www.furrygoat.com/index.html"));
GUID guidNetworkObject;
DWORD dwIndex = 0;

if(ConnMgrMapURL(tchRemoteUrl, &guidNetworkObject, &dwIndex)
      == E_FAIL) {
      OutputDebugString(TEXT("Could not map the request to a
         network identifier"));
return FALSE;
}

// Now that we've got the network address, set up the
// connection structure
CONNMGR_CONNECTIONINFO ccInfo;

memset(&ccInfo, 0, sizeof(CONNMGR_CONNECTIONINFO));
ccInfo.cbSize = sizeof(CONNMGR_CONNECTIONINFO);
ccInfo.dwParams = CONNMGR_PARAM_GUIDDESTNET;
ccInfo.dwFlags = CONNMGR_FLAG_PROXY_HTTP;
ccInfo.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE;
ccInfo.guidDestNet = guidNetworkObject;

// Make the connection request (timeout in 5 seconds)
if(ConnMgrEstablishConnectionSync(&ccInfo, &hConnection,
      dwTimeout, &dwStatus) == E_FAIL) {
      return FALSE;
}

// Connection has been made, continue on...

Creating an asynchronous connection is a bit more involved. To make the connection request, you can use the ConnMgrEstablishConnection() function, which is defined as follows:

HRESULT WINAPI ConnMgrEstablishConnection(
   CONNMGR_CONNECTIONINFO *pConnInfo, HANDLE *phConnection);

The first parameter, pConnInfo, is a pointer to a CONNMGR_CONNECTIONINFO structure that describes the connection. When the function returns, phConnection will point to a connection handle for the request.

When you are finished using the connection, you must properly free the handle by calling the ConnMgrReleaseConnection() function, as described in the section "Disconnecting from an Active Connection," later in this chapter.

The following code polls the request handle for status changes:

  // Establish an asynchronous connection
  HANDLE hConnection = NULL;
  DWORD dwStatus = 0;

  // Get the network information where we want to establish a
  // connection
  TCHAR tchRemoteUrl[256] = TEXT("\0");
  wsprintf(tchRemoteUrl, TEXT("http://www.furrygoat.com/index.html"));
  GUID guidNetworkObject;
  DWORD dwIndex = 0;

  if(ConnMgrMapURL(tchRemoteUrl, &guidNetworkObject, &dwIndex)
        == E_FAIL) {
     OutputDebugString(TEXT("Could not map the request to a
        network identifier"));
     return FALSE;
  }

  // Now that we've got the network address, set up the
  // connection structure
  CONNMGR_CONNECTIONINFO ccInfo;

  memset(&ccInfo, 0, sizeof(CONNMGR_CONNECTIONINFO));
  ccInfo.cbSize = sizeof(CONNMGR_CONNECTIONINFO);
  ccInfo.dwParams = CONNMGR_PARAM_GUIDDESTNET;
  ccInfo.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE;
  ccInfo.guidDestNet = guidNetworkObject;

  // Make the connection request
  if(ConnMgrEstablishConnection(&ccInfo, &hConnection) ==
        E_FAIL)
     return FALSE;

  // Poll to see if the connection has been established
  BOOL fLoop = TRUE;
  BOOL fConnected = FALSE;

  while(fLoop) {
     dwStatus = 0;
     if(FAILED(ConnMgrConnectionStatus(hConnection,
          &dwStatus))) {
        // Do some error processing here
     fLoop = FALSE;
     break;
  }

  // Got the status, do something with it:
  if(dwStatus & CONNMGR_STATUS_CONNECTED) {
     OutputDebugString(TEXT("Connected!"));
     fLoop = FALSE;
     fConnected = TRUE;
     break;
  }

  if(dwStatus & CONNMGR_STATUS_WAITINGCONNECTION)
     OutputDebugString(TEXT("Establishing a
          connection...."));

  if(dwStatus & CONNMGR_STATUS_DISCONNECTED) {
     OutputDebugString(TEXT("Disconnected from the
          network...."));
     fLoop = FALSE;
   }
}

// Release the handle gracefully
if(!fConnected && hConnection) {
   if(ConnMgrReleaseConnection(hConnection, FALSE) == S_OK)
      hConnection = NULL;
   return FALSE;
}

// Connection has been made, continue on...

Getting the Connection Status

To get the status of a Connection Manager connection, you can use the ConnMgrConnectionStatus() function:

HRESULT WINAPI ConnMgrConnectionStatus(HANDLE hConnection,
   DWORD *pdwStatus);

The function needs only the handle to a current connection, and will return the status code for it in the pointer specified by the pdwStatus parameter.

Table 7.4 shows the possible status values that the Connection Manager can return.

Table 7.4. Connection Manager Status Values

Value

Description

CONNMGR_STATUS_UNKNOWN

Unknown.

CONNMGR_STATUS_CONNECTED

Connected.

CONNMGR_STATUS_DISCONNECTED

Disconnected.

CONNMGR_STATUS_CONNECTIONFAILED

Connection has failed and cannot be reestablished.

CONNMGR_STATUS_CONNECTIONCANCELED

User-aborted connection.

CONNMGR_STATUS_CONNECTIONDISABLED

Connection is ready to connect but disabled.

CONNMGR_STATUS_NOPATHTODESTINATION

No path could be found to the destination.

CONNMGR_STATUS_WAITINGFORPATH

Waiting for a path to the destination.

CONNMGR_STATUS_WAITINGFORPHONE

Voice call is in progress.

CONNMGR_STATUS_WAITINGCONNECTION

Attempting to connect.

CONNMGR_STATUS_WAITINGFORRESOURCE

Resource is in use by another connection.

CONNMGR_STATUS_WAITINGFORNETWORK

No path to the destination could be found.

CONNMGR_STATUS_WAITINGDISCONNECTION

Connection is being brought down.

CONNMGR_STATUS_WAITINGCONNECTIONABORT

Aborting connection attempt.

Connection Priorities

One of the Connection Manager's most useful features is its ability to juggle multiple requests, i.e., from more than one application at the same time. When making a connection request for your application, you are required to set its priority level (the dwPriority member of the CONNMGR_CONNECTIONINFO structure). This enables the Connection Manager to effectively schedule the order in which each request is processed?connection requests that have a higher priority are handled before those with a lower one.

Once a connection has been established, you can manually change its priority at any time by calling the ConnMgrSetConnectionPriority() function:

HRESULT WINAPI ConnMgrSetConnectionPriority(HANDLE
   hConnection, DWORD dwPriority);

The first parameter is the request handle you were returned from either the ConnMgrEstablishConnection() or ConnMgrEstablishConnectionSync() function. This is followed by the new priority level you want to set for the request. The list of possible values is the same as the list for the dwPriority member of the CONNMGR_CONNECTIONINFO structure.

A well-behaved application will change its connection priority based on what the user is currently doing on the device. For example, if an application is downloading a Web page, you would want to set the connection to a high-priority level such as CONNMGR_PRIORITY_USERINTERACTIVE. This provides users with a better experience, because they expect an immediate response and high-priority requests are favored by the Connection Manager. However, if a user switches to a different application or is idle for an extended period of time, you will want to switch the level to CONNMGR_PRIORITY_USER_IDLE. A lower priority level enables other processes to more effectively share the connection.

The following code sample shows how to manually change the priority for a connection:

if(FAILED(ConnMgrSetConnectionPriority(hConnection,
   CONNMGR_PRIORITY_USERIDLE))) {
      OutputDebugString(TEXT("Could not change connection
           priority.."));
      return FALSE;
}

Disconnecting from an Active Connection

To close a connection request, you can simply call the following function:

HRESULT WINAPI ConnMgrReleaseConnection(HANDLE hConnection,
  BOOL bCache);

The hConnection parameter should be set to the current connection you want to release. If this is the last request handle that the Connection Manager has for the network type with which you are connected, it will drop the connection; otherwise, it will be left open for any other active requests. The bCache parameter should be set to TRUE if you want the Connection Manager to remember the connection in its cache; otherwise, you should set this to FALSE.