How the Compact Framework Addresses Accessing Remote Data

Just as the desktop Framework does, the Compact Framework provides a variety of network communication mechanisms that access remote data. The existence of these classes is important. As pointed out in the introduction to this chapter, the capabilities provided in this area are imperative. Without them, you can take the word "mobile" out of "mobile application" in a Compact Framework scenario. Table 4-2 highlights the various ways connections can be made and the namespaces in which developers will find the classes necessary to create the connection.

Because the mechanisms exist at different levels of complexity, this section starts at the easier, or higher-level, classes and works down to the lower layers. Another way to position the classes is the dependency relationship. In other words, the higher layers depend on the functionality implemented in the lower layers, as shown in Figure 4-3. The remainder of this section will look at the various ways applications can retrieve remote data.

Figure 4-3. Communication Dependencies. This diagram positions the communication types in a typical pancake fashion, highlighting the dependencies between the types.

graphics/04fig03.gif

XML Web Services

As discussed in Chapter 2, Microsoft has helped (with the aid of IBM) to push the idea of XML Web Services into the wider world. This is one of the most hyped concepts surrounding the .NET Framework with good reason. To introduce this subject, the benefits of XML Web Services will be considered, and the important pieces of the XML Web Services architecture will be highlighted. Finally, XML Web Services will be explored in the context of SDP by looking at Compact Framework's support and how VS .NET helps the SDP developer. To conclude this section, an example will be reviewed and other related programmatic issues mentioned.

Table 4-2. Communication Types and Where They Are Exposed in the Compact Framework

Communication Type

Namespace

XML Web Services

System.Web.Services

SQL Server

System.Data.SqlClient

Web Client

System.Net

Sockets

System.Net.Sockets

TCP

System.Net.Sockets

UDP

System.Net.Sockets

Infrared

System.Net.Sockets

General Benefits of Web Services

By supporting XML Web Services, application-to-application (A2A) communication be can easily achieved. This concept is best illustrated by the platform, language, and device independence discussed in Chapter 2 due to the usage of widely adopted standards like HTTP and XML. However, other benefits exist.

First, one of its biggest benefits is that it makes an enterprise's existing business logic, which may have been tied to a single platform, available to other platforms (internally and externally). In the Windows world, consider the scenario where COM+ objects can now be more easily exposed to internal or external clients for other platforms to use.[3]

[3] In fact, Windows Server 2003 (COM+ 1.5) includes integrated support for exposing COM+ components, as XML Web Services.

graphics/key point_icon.gif

Second, the separation of business and data-access logic from presentation logic is always a desirable goal when developing a solution. Moreover, this principle can naturally be applied to application development with Web Services. For example, business logic encapsulated in XML Web Services is naturally partitioned from any presentation logic that accesses it. This naturally allows organizations to develop solutions based on a service-oriented architecture (SOA) model, as proscribed by Microsoft's patterns and practices group.[4] In addition, exposing a Web Service makes it simpler for other applications to retrieve data, rather than having to resort to HTML screen scraping and other fragile techniques.

[4] See the reference to the Microsoft document on the subject in the "Related Readings" section at the end of this chapter.

Third, the communication protocols used with XML Web Services, primarily HTTP, allow Web Service calls to pass through firewalls where other protocols such as DCOM typically have difficulty. Further, emerging standards such as WS-Security[5] allow for secure authentication and encryption when communicating with a Web Service.

[5] Microsoft has released the Web Services Extensions (WSE) for the .NET Framework, which implements several of the Global XML Web Services Architecture (GXA) specifications jointly developed by Microsoft and IBM, including WS-Security.

Finally, for VS .NET developers, the creation and consumption of XML Web Services are simple tasks that allow the developer to concentrate on application logic instead of the plumbing of Web Services. As will be discussed shortly, both the desktop and Compact Framework provide the classes for handling all of the plumbing issues, including custom authentication, serializing and deserializing the request and the response, and more. Creating a Web Service client is also simple in VS .NET due to its code-generation ability, which will be discussed as well.

Web Services Architecture

An XML Web Service is basically a programmable application component that is accessible via standard Internet protocols. Another way to look at a Web Service is that it is a Web page with no HTML, just data with embedded structure. The architecture of Web Services includes working with a directory of Web Services, performing a discovery of the Web Service, retrieving a description of the Web Service, and then invoking a method on the Web Service, using a known message format.

  • Universal Description Discovery and Integration (UDDI)?the Web Service Directory: Supported by the likes of Microsoft, IBM, SAP, and other companies that belong to the UDDI Consortium (www.uddi.org), the UDDI specification provides the ability to graphically and programmatically search a database of registered XML Web Services. Replicated directories are hosted by the aforementioned vendors. For example, see http://uddi.microsoft.com for Microsoft's hosted directory.

  • DISCO?Discovering the Web Service descriptions: XML Web Service discovery is the activity of programmatically inquiring for documents that describe the Web Service, including documentation and WSDL (see below). By searching for .disco files on an IIS Web server using a command-line tool or the VS .NET Add Web Reference dialog, clients can discover the endpoints for Web Service on a particular Web server. It should be noted, however, that DISCO is a Microsoft technology that has not been widely adopted and appears to be withering on the vine. For more information about DISCO, see the article referenced in the "Related Reading" section at the end of the chapter.

  • WSDL?Describing the Web Service: WSDL is an XML grammar that describes the Web Service, including its method names, input parameters and their types, and the response. It is analogous to the metadata found in Compact Framework assemblies and type libraries used in COM. WSDL is an industrywide specification recommended by the W3C.[6] Version 1.1 is implemented by the Framework, although the draft version of 1.2 was made available in March 2003.

    [6] The specifications can be found on the Web Services Description Working Group's Web page at www.w3.org/2002/ws/desc.

  • Invocation and the message format: SOAP is the XML invocation and message format specification. SOAP is made up of four parts: the requirement of using an envelope to contain the message (the only mandatory requirement), optional rules for encoding and serializing application-specific data, an optional Remote Procedure Call (RPC)-style messaging system (SOAP is really a one-way messaging system), and an optional specification on how to bind the messages to HTTP. Although Web Services are typically invoked via SOAP, they are not required to be. Theoretically, any protocol can be used to transport a Web Service message. However, this limits the use of a Web Service to clients that can utilize the protocol of the hosting platform. So, the main protocols of interest are SOAP, HTTP-GET, and HTTP-POST. Fortunately, VS .NET and the Framework support each of these natively.

XML Web Service and SDP

The good news for SDP developers is that the Compact Framework supports the consumption of XML Web Services. Furthermore, the process of creating a Web Reference in an SDP is identical to that in a Windows Forms or ASP.NET application. Because of this similarity, a developer can leverage his existing VS .NET and Framework knowledge while developing an SDP application. This also implies that smart devices will get all of the benefits described in the earlier section on general benefits.

However, as mentioned in Chapter 2, creating XML Web Services is not supported by the Compact Framework because it does not include support for ASP.NET, and Windows CE does not include a Web server.

Creating a Proxy Class for Compact Framework Consumption

In order to call a Web Service without the help of a tool such as VS .NET, a developer would have to go to a great deal of effort. Not only would the developer have to manage XML conversion of the data and messages, he would also have to deal with transmission logic and authentication. Fortunately, VS .NET provides a solution by exposing a code-generation wizard.

VS .NET provides the "Add Web Reference" menu option, which creates a proxy class to encapsulate the invocation of the Web Service. This functionality is purposely analogous to the Add Reference process used to reference code in a local assembly or COM component. In the case of Web Services, however, adding a Web reference indicates the intention of invoking remote code hosted in an XML Web Service.

Once invoked, the resulting dialog shown in Figure 4-4 includes a single step to identify the endpoint (URL) of the Web Service via a known address or searching the UDDI directory. When the host is identified, VS .NET downloads the service's WSDL that describes the Web Service and runs it through a code generator (also exposed in the Wsdl.exe command-line utility) to create a proxy class complete with public methods that map to the service operations and through which the service can be called.

Figure 4-4. Adding a Web Reference. This dialog allows a developer to reference a Web Service and downloads the WSDL used to create the proxy class.

graphics/04fig04.gif

Once built, the proxy class shows up in the Solution Explorer of VS .NET. The class resides in a namespace that reflects the endpoint of the service, although it can be renamed to something more reflective of its function. By examining the proxy class in the Object Browser or Class View window in VS .NET, developers will see that the functionality exposed by the service is now present in the proxy class. In addition to methods used to invoke each operation of the Web Service, the proxy class includes asynchronous function names that start with "BeginXXX" and "EndXXX," where "XXX" is one of the operations exposed by the Web Service.

Furthermore, the proxy class inherits from the SoapHttpProtocol class (and its parents), which includes a core set of properties, including those shown in Table 4-3.

Once the proxy class is built, it is simple to work with the Web Service. The developer would instantiate an object of the proxy class and then call one of the methods. The proxy class handles the rest of the details, including serializing any arguments passed to the service, creating the XML to invoke it, pushing the request to the server hosting the Web Service, and deserializing the response. An additional benefit of using the proxy class is VS .NET's IntelliSense and the compile-time checking against the Web Service method usage. The following code snippet invokes a Web Service that returns an ADO.NET DataSet object:


' Instantiate proxy object
Dim myService As New ServiceHost.NeededService myService.Url = Settings.ServiceEndPoint
' Send request to server and store response in a DataSet
DataSet ds = myService.GetSomeInfo()

Table 4-3. Properties Exposed by the Client Proxy Class Used to Call the XML Web Service

Property

Description

Url

This property defaults to the value of the endpoint used during creation. It allows the application to override at runtime and is often stored in a configuration file or the registry for ease of maintenance.

Timeout

This property defaults to 100,000 ms (100 seconds) and provides a way for the client to specify how long to wait for synchronous method invocations to the Web Service before throwing an exception.

Credentials

This property provides for the passing of client credentials to the Web server when the server is configured to use Basic or Windows (IIS only) authentication.

ClientCertificates

This property provides for the passing of Authenticode X.509 certificates to the server.

Proxy

This property allows for the client to specify credentials for getting through a firewall that is blocking access out to the Internet.

Final Thought on Web Services

If you would like to see some more samples of Compact Framework applications taking advantage of Web Services, there are several good examples out on the Internet with source code, including the following:

  • The MapPoint .NET sample[7] application for devices: This application allows users to retrieve interactive maps and driving directions and to find points of interest such as hotels or service stations on their Pocket PCs. MapPoint .NET is Microsoft's first commercially available XML Web Service, providing location intelligence to Web-based, mobile, enterprise, or traditional desktop applications.

    [7] See www.gotdotnet.com/team/netCompactFramework/mappoint.

  • AskDotNet[8]: This is an end-to-end sample application for conducting field surveys with devices featuring the .NET Compact Framework, ASP.NET Mobile Controls, and XML Web Services.

    [8] See www.gotdotnet.com/team/netCompactFramework/askdotnet.

Web Services are a natural progression in distributed application architectures. By using the Compact Framework, which supports the consumption of Web Services, your SDP applications can communicate with a variety of back-end systems.

Accessing SQL Server Remotely

A second way to access data remotely is by using the .NET Data Provider that ships with the Compact Framework and is exposed through the System.Data.SqlClient namespace for working with SQL Server.

SQL Server is one of the leading database servers today. As a result, the SqlClient .NET Data Provider is familiar to many desktop and server programmers who work with the .NET Framework because SQL Server is used in Windows Forms, Enterprise Services, and ASP.NET applications today. SQL Server is also well known to those who work with ADO 2.x in VB 6.0 and ASP. Moreover, many developers interested in the Compact Framework will be members of organizations that rely on Microsoft technologies and, therefore, probably have SQL Server databases implemented in important applications. So, a normal transition to smart device architectures will be to access these already established data sources.

As discussed in Chapter 3, ADO.NET is the technology that Framework developers use for accessing data in data sources like database servers. Rooted in the System.Data namespace, ADO.NET provides a model that does several things for the developer, including the combination of relational and object-oriented paradigms (OOPs), integration with XML, and the abstraction of database server protocols through .NET Data Providers.

There are several benefits to the way ADO.NET defines data providers. One benefit is that the class model for providers in the desktop Framework is the same as that in the Compact Framework. Therefore, the knowledge of data providers in the .NET Framework can be leveraged except for the actual database implementation differences and smart device constraint considerations. A second benefit is that Compact Framework SqlClient is identical to the desktop Framework implementation of SqlClient. A third benefit is that other data providers that are more easily understood can be built in the Compact Framework. In the case of Compact Framework, the SQL CE team provided a rich data-access mechanism in System.Data.SqlServerCe, a data provider for SQL Server 2000 Windows CE Edition (SQLCE) database residing on the device and which will be described in detail in Chapter 5.

ADO.NET Data Providers are required to implement certain interfaces. Therefore, all providers will have managed classes for connections, commands, parameters, data adapters, command builders, data readers, transactions, exceptions, and more. Missing from this is the DataSet and its related classes because data sets are provider-independent.

In the remaining parts of this section SqlClient will be explored and considerations related to usage on a constrained device discussed.

The SqlClient Provider Architecture

To get a feel for the SqlClient provider, consider the diagram shown in Figure 4-5. Because of the common .NET Data Provider architecture, certain interfaces must be implemented as depicted in the diagram. Again, this is the same as the SqlClient model for the desktop Framework.

Figure 4-5. SqlClient .NET Data Provider. This dialog illustrates the architecture of the SqlClient provider.

graphics/04fig05.gif

graphics/key point_icon.gif

One of the primary advantages of using the SqlClient provider is that it performs very well because it creates Tabular Data Stream (TDS) packets to communicate in SQL Server's native protocol, thereby cutting out any unnecessary layers in the process. This sort of "narrow" provider, which can be used against only one data source, can be differentiated from the "broad" providers, such as those for OLE DB and ODBC, that allow access to multiple data sources and that ship with the desktop, but not with the Compact Framework. Another example of a narrow provider is the Oracle provider that ships with the desktop Framework.

Because many books and articles have been written on ADO.NET and the various aspects of using SqlClient (we shamelessly recommend one of our own books in the "Related Reading" section at the end of the chapter), the discussion will now focus on using SqlClient in SDP applications.

SqlClient on a Smart Device

There are several issues to consider when looking at using SqlClient on a smart device.

The first issue to address is whether to use SqlClient or SQLCE. While other chapters will go into more detail on the features of SQLCE, it is important to understand that there are times when accessing a remote SQL Server is a good choice.

For example, consider the following scenario. The device is capturing data in a manufacturing environment, and the data is stored on a SQL Server 2000 database. The business requires the captured data to be placed in the SQL Server database as soon as possible. In most cases, this database exists and is being fed by an existing Web or desktop application. If the device can maintain a full-time high-speed connection to the local network, this scenario works great. We see this in data-collection environments like manufacturing, warehousing, medical, and hospitality.

A second scenario is where the data is too large for the smart device. In this case complex queries can be made to the server that return smaller and more manageable sets of data. This still allows for sending requests to a rich query system provided by SQL Server and also allows for the device to maintain a low-memory footprint. Again, the key requirement for scenarios such as these is a reliable connection to the network, especially in communications with the database.

Although there are scenarios for which SqlClient in a Compact Framework is well suited, there are some limitations to consider:

  • The most obvious is that mobility is limited due to the dependency of a full-time, high-speed, reliable network connection.

  • For scenarios where devices would access the Internet, the SQL Server has to be exposed to the Internet. Consequently, well-documented firewall ports will have to be opened. However, there are options to change to other ports besides the default (1433).

  • Automatic port discovery is not supported. SqlClient has to have the port number defined in the connection string when connecting to a named server instance or not using the default port, 1433.

  • graphics/key point_icon.gif In applications where there are periods of no connectivity, some additional programming effort will be required. The downloaded data will have to be cached using a DataSet, as shown in Chapter 3. When the time comes for updating the SQL Server, a connection will have to be made and transmission code executed. These are simple tasks, but the plumbing around good synchronization takes additional effort. Synchronization is not only just the act of exchanging data, but also of providing for error recovery and related issues. This is a primary reason for using SQLCE, which provides built-in synchronization, as discussed in Chapter 7.

  • Because the client is connecting directly to the database, scalability of the database is reduced, because this is a two-tier architecture. This can be a factor when a large number of devices is deployed in an environment like a corporate campus or hospital.

  • Encryption is not supported. If the database server even requires a certificate, an exception will be thrown. One secure way, however, of connecting remotely to SQL Server is to use a VPN, as discussed in Chapter 9.

  • Even though SqlClient in the desktop Framework can communicate over several different protocols (TCP/IP, Named Pipes, AppleTalk, Banyan, NWLink, IPX/SPX, and VIA) due to the underlying Net-Library, SqlClient in the Compact Framework supports only TCP/IP. Therefore, the SQL Server must have this protocol enabled.

  • There is no connection pooling on a smart device. This is to be expected because a mobile device is used by one user at a time.

One last and important point regarding SqlClient in the Compact Framework is that even though it does not support collecting credentials using the Windows Security Support Provider Interface (SSPI), it does allow for Windows account credentials to be passed in the connection string, thus not requiring SQL Security to be configured on the server. To do this, the developer has to use the Integrated Security parameter, as in the desktop Framework, and also include the User Id and Password attributes as well, as shown in Listing 4-1.

Listing 4-1 A Simple Example of Using SqlClient and a DataSet.
Imports System.Data
Imports System.Data.SqlClient

Private Function GetPublishersDS()
    Dim ds As New DataSet
    Dim strCn as String = "Server=10.0.0.1;Database=Atomic;" & _
      "Integrated Security=SSPI;User Id=STL/PUJOLS;Password=MVP"
    Dim sqlConn As New SqlConnection(strCn)
    sqlConn.Open()

    Dim sqlDA As New SqlDataAdapter("SELECT * FROM products", sqlConn)

    sqlDA.Fill(ds)
    sqlConn.Close()

    return ds
End Function
Using Data Readers

Data readers are an important part of ADO.NET as they provide a fast and efficient way to access data. Simply defined, data readers are mechanisms designed to retrieve forward-only, read-only result sets from a data source. Unlike DataSet objects, data readers provide a mechanism to stream through data quickly without caching it for future use. Several of the other differences between data readers and the data sets are summarized in Table 4-4.

In the SqlClient provider the data reader is exposed in the SqlDataReader class that implements the IDataReader and IDataRecord interfaces. IDataReader provides important functionality, most notably the Read method, which retrieves the next record, while IDataRecord provides the methods that allow access to the data in the current record.

Table 4-4. Differences between the DataSet and Data Readers

Data Set

Data Reader

Is independent of any provider

Is specific to the provider

Stores heterogeneous data (i.e., from multiple sources)

Retrieves data from a single source

Uses data adapter for updates

Is read-only; uses Command object and ExecuteNonQuery

Does not require a persistent connection

Requires a connection while open

Can be inherited from using typed DataSet objects

Is weakly typed

Performs well, but requires extra resource for storage of the entire result set

Shows best performance; no resource consumption; developer responsible for storage of result set

Can be serialized to XML and passed to and returned from a Web service

Cannot be serialized and passed remotely

Data binding supported

Data binding not supported

Incidentally, the Read method of the IDataReader interface addresses a common issue that ADO programmers often encounter when using the ADO Recordset object. Because the Read method both returns False when the result set has been fully processed and moves to the next record, there is no possibility of forgetting to move the cursor, which often happened when the MoveNext method of the RecordSet was forgotten. An example of opening and traversing a SqlDataReader is shown in Listing 4-2.

Listing 4-2 Retrieving Data Using the SqlDataReader.
Private Function GetTeamsForLibertyBowl() As ArrayList
  Dim al As New ArrayList()
  Dim dr As SqlDataReader
  Dim cn As New SqlConnection(_connection)
  Dim cm As New SqlCommand(teamsSql, cn)

  cn.Open()
  dr = cm.ExecuteReader(CommandBehavior.CloseConnection)

  Do While dr.Read()
    al.Add(New TeamStruct(dr["teamId"], dr["teamName"]))
  Loop

  dr.Close()

  Return al
End Function

As mentioned in Table 4-4 and illustrated in Listing 4-2, the lifetime of a data reader is dependent on the lifetime of the connection, whereas the DataSet remains even after the connection is closed. In fact, the data adapter classes used to populate DataSet objects, such as the SqlDataAdapter, use data readers internally to populate the DataSet. This is also evident in the fact that a data reader is created only through the ExecuteReader and does not provide a public constructor.

Another comparison between DataSet objects and data readers is in the area of binding. As discussed in Chapter 3, controls that can be bound in the Compact Framework must implement the IList interface or an interface that can return an IList interface like IListSource. The DataSet class does implement IListSource, but data reader classes like SqlDataReader do not.

Finally, you'll notice that the CommandBehavior.CloseConnection argument can be passed to the ExecuteReader method. This method ensures that the database connection is closed when the data reader is closed and is essential for passing the data reader around within an application, such as when returning it from a method. Furthermore, the connection object cannot be used for other actions until the Close method is called.

To summarize this discussion, a short list of reasons to use a DataSet over a data reader include the following:

  • Deferred updating of data at the database

  • The retrieval of hierarchical data or from multiple sources that will be related

  • Local persistence for later retrieval using the WriteXml and ReadXml methods

  • Binding to Window Forms controls

As a result, if these requirements are not important, a data reader would be more appropriate, especially if the following are true:

  • Fast access to forward-only read-only data is required.

  • The data will be used immediately and not persisted.

Because devices typically operate in an occasionally connected fashion, Compact Framework developers typically use DataSet objects more frequently than data readers.

Other Issues to Consider When Using SqlClient for Data Access

Mature practices require that developers utilize defensive programming, especially in areas where the application is transferring data to remote systems. The ADO.NET architecture provides for exception classes specific to the data provider; thus SqlClient includes the SqlException class that acts as a container for one or more SqlError objects. SqlException objects are created when SQL Server encounters an error not on the client side, which would result in standard Framework exceptions.

To handle database errors gracefully, the listings shown in this chapter should include SEH code using a Try-Catch block. So, for example, the code in Listing 4-1 could be rewritten as shown in Listing 4-3 to include a Try block. Multiple Catch blocks exist so errors can be appropriately handled and in this case logged by a function specifically coded for that error. This can be done because not all exception classes are the same. For example, the SqlException class can contain multiple errors, which are stored in a collection of System.Data.SqlClient.SqlError instances. The last Catch block will catch the remaining error types due to the use of the base class System.Exception. And last, the Finally block will execute whether an error occurs and can be used to clean up database connections and deallocate other resources.

Listing 4-3 An Example of Using the Timeout Property and SEH.
[View full width]
Private Function GetDS() As DataSet
  Dim ds As New DataSet
  Dim cn As SqlConnection
  Dim da As SqlDataAdapter
  Dim strCN As String = "Server=10.0.0.1;Database=Atomic;" & _
graphics/ccc.gif "UID=Musial;PWD=StanTheMan;Connect Timeout=10"

  Try
    cn = New SqlConnection(_connect)
    da = New SqlDataAdapter(teamSql, cn)
    da.Fill(ds)
  Catch ex As SqlException
    LogSqlError("GetDS", ex)
  Catch ex As Exception
    LogError("GetDS", ex)
  Finally
    If (Not cn Is Nothing) Then cn.Close()
  End Try

  Return ds
End Function

Also, keep in mind that the SqlError classes expose a Class property that SQL Server database administrators refer to as "severity level." If an error occurs with a severity level of 20 or higher, the server will close the connection. If the developer is maintaining a global connection object, this will be an issue.

Another note about Listing 4-3 is the controlling of the Timeout value in the connection string. The default time-out for the creation of a connection is 20 seconds, but this can be overridden in the connection string. A value of zero implies an infinite time-out and is therefore not recommended. In this example, the default value has been overridden and set to ten seconds.

Final Considerations When Using SqlClient

graphics/key point_icon.gif

SqlClient is a well-known data provider for those who already use SQL Server, and it works well. However, it should be used in the right situations. Considerations include scenarios where there is the need to access data that is larger than the smart device can store or where the data does not require future updating or searching. The scenario would further require the environment to have full-time, reliable, high-speed connectivity. Due to these requirements, the SqlClient provider is not recommended for disconnected or occasionally connected applications.

For these types of applications, there are better alternatives. For example, XML Web Services might be considered a better architecture because the application will gain scalability due to accessing the middle tier. However, this option still lacks built-in synchronization logic. The premier disconnected architecture will utilize SQLCE and the SqlServerCe provider, as discussed in Chapter 7.

Using Pluggable Protocols

For simple interactions with servers, the Compact Framework provides a layered and extensible mechanism called Pluggable Protocols. This is a class model based on the abstract classes in the System.Net namespace, WebRequest and WebResponse. Concrete classes, which inherit from these abstract classes, are registered as supporting a specific protocol.

As Listing 4-4 shows, the WebRequest.Create factory method returns the registered concrete class for that protocol. In this example, an HttpWebRequest reference is returned because the Uniform Resource Identifier (URI) passed into the Create method contains the prefix HTTP. As it turns out, the only pluggable set in Compact Framework is the HTTP pair HttpWebRequest and HttpWebResponse, which inherit from WebRequest and WebResponse, respectively. This model allows for the addition of other protocols, like FTP, WebDAV, or NNTP. Although the desktop Framework includes FileWebRequest and FileWebResponse, these are not supported in Compact Framework. If an unregistered protocol is used, a NotSupportedException is generated from the WebRequest.Create method.

Listing 4-4 Calling a Web Server. This method sends a request to a Web server and displays the raw response in a Message Box.
Imports System.Net
Imports System.IO

 Private Sub GetAtomicNews()
    ' Create URI
    Dim uri As New Uri("http://atomic.quilogy.com")

    ' Returns a HttpWebRequest based on URI
    Dim wreq As WebRequest = WebRequest.Create(uri)

  Try
    ' Send request to specified URL
    Dim wresp As WebResponse = wreq.GetResponse()

    ' Set up StreamReader by connecting to WebResponse stream
    Dim sr As New StreamReader(wresp.GetResponseStream())

    ' Read the entire stream into a string
    Dim respmsg As String = sr.ReadToEnd()

    ' Display response
    MsgBox(respmsg)
  Catch e As Exception
    ' Handle errors
  Finally
    sr.Close()
    wresp.Close()
  End Try

End Sub

Using the pluggable protocols, developers can build or obtain additional request and response classes to extend their applications to use these protocols. By programming against the base classes, developers can therefore write polymorphic code that is reusable.

Direct Communication with Sockets

Sometimes developers need to communicate with other systems with a low-level protocol. If a developer wanted to work at the lowest level, then sockets-based programming is the place to start. In the simplest terms, a socket is one side of a two-way communication link that exists between two machines on a network. In programming terms, sockets are an abstraction of Internet and other protocols, as shown in Figure 4.6. Providing applications the ability to view network communications as a stream, sockets give the ultimate flexibility in communications across different low-level Internet protocols like TCP, UDP, and others. For example, in some enterprise environments, communication with legacy systems can be accomplished using sockets to make requests and return results via TCP or UDP.

Figure 4-6. Sockets as an Abstraction. This dialog illustrates that sockets are an abstraction used by developers to access Internet and other protocols.

graphics/04fig06.gif

The Compact Framework, like its desktop cousin, provides the System.Net.Socket class to serve as a wrapper around the native operating system support for networking. All other network-access classes in the System.Net namespace are built on top of this managed-socket implementation.

Developers familiar with the PC programming environment know that WinSock is a library that allows socket-based communications for the desktop and server environment. Consequently, Windows CE also has an implementation of WinSock. Just like the PC implementation, WinSock for CE supports the stream and datagram models. The datagram model is used for sending discrete packets to specific addresses; however, this section will focus on the more common stream model.

When working with sockets in a stream scenario, some effort is required to manage a connection. When instantiating a Socket object, there are several parts of the constructor, including AddressType, SocketType, and ProtocolType. These are all System.Net.Sockets enumerations that provide a variety of networking choices, thus illustrating that the managed-sockets abstraction covers a variety of communication types in the Internet world. As Figure 4-7 shows, the server application has to do a little more work than the client. These steps are typical of any socket library and are shown in Listing 4-5.

Listing 4-5 A Socket Example of Server Implementation. This method listens for incoming messages.
Imports System.Net
Imports System.IO

Public Shared Sub GetSomeData()
    Dim strData As String

    ' Data buffer for incoming data.
    Dim bytes() As Byte = New [Byte](1024) {}

    Dim ipHostInfo As IPHostEntry = Dns.Resolve(Dns.GetHostName())
    Dim ipAddress As IPAddress = ipHostInfo.AddressList(0) 
    Dim localEndPoint As New IPEndPoint(ipAddress, 12000)

    ' Create a TCP/IP socket.
    Dim listener As New Socket(AddressFamily.InterNetwork, _
     SocketType.Stream, ProtocolType.Tcp)
 
    ' Bind the socket AND listen
    Try
      listener.Bind(localEndPoint)  
      listener.Listen(10)

      ' Start listening for connections. 
      While True
        Dim handler As Socket = listener.Accept()
        strData = String.Empty
        bytes = New Byte(1024) {}
        Dim bytesRec As Integer = handler.Receive(bytes)
        strData = Encoding.ASCII.GetString(bytes, 0, bytesRec)

        ' Close connection
        handler.Shutdown(SocketShutdown.Both)
        handler.Close()
      End While

    Catch e As Exception
      MsgBox(e.ToString())
    End Try

    If Not listener Is Nothing Then listener.Close()
End Sub
Figure 4-7. Steps Required in a Sockets Application. This diagram illustrates the steps required of the client and server in order to exchange data using sockets.

graphics/04fig07.gif

The client implementation is easier than the server coding and would look as follows:


Dim ipHostInfo As IPHostEntry = Dns.Resolve(hostName)
Dim ipAddress As IPAddress = ipHostInfo.AddressList(0)
Dim ipe As New IPEndPoint(ipAddress, 11000)

Try
  s.Connect(ipe)
Catch se As SocketException
  ' Handle the error
End Try

Dim msg As Byte() = _
  System.Text.Encoding.ASCII.GetBytes("Some data to send")
Dim bytesSent As Integer = s.Send(msg)

However, the Compact Framework also includes a higher-level set of classes to work with for doing this type of communication, as the next section discusses.

Communicating with TCP and UDP

TCP and UDP are the most popular Internet protocols today. They are so widely used that the Compact Framework provides managed representations of them. Interestingly, these classes are based on and use the synchronous methods of the Socket class and provide an abstraction model that most developers will choose over using the Socket class directly.

TCP is more popular than UDP because TCP is responsible for ensuring that data packets are sent to the endpoint and assembled in the correct order when they arrive and because individual UDP messages are limited to 16K. TCP support comes in two flavors: TcpClient and TcpListener. These classes use the NetworkStream class, allowing the developer simply to manipulate the stream using Read and Write methods. See Listing 4-6 for a TcpClient example.

Listing 4-6 Working with the TcpClient Class. This method sends data to another server.
Imports System.Net
Imports System.Net.Sockets

Shared Sub SendMsg(server As String, port as Integer, _
   message As String)

 Dim client As TcpClient
 Try
   client = New TcpClient(server, port)

   'Encode message to ASCII
   Dim data As [Byte]() = _
            System.Text.Encoding.ASCII.GetBytes(message)

   ' Get a client stream for writing.
   Dim stream As NetworkStream = client.GetStream()

   ' Send the message to the TcpServer.
   stream.Write(data, 0, data.Length)

   data = New [Byte](256) {}

   Dim responseData As [String] = [String].Empty

   ' Read the first of the response bytes.
   Dim bytes As Int32 = stream.Read(data, 0, data.Length)
   responseData = _
            System.Text.Encoding.ASCII.GetString(data, 0, bytes)

 Catch e As SocketException
         ' Log socket error
 Finally
   ' Close local port
   If not client is nothing Then client.Close()
 End Try
End Sub

Looking at Listing 4-6, one can see that TcpClient usage is straightforward. The constructor defines the local endpoint that expects to get TCP requests. After getting a NetworkStream reference from the GetStream method, the stream can be filled with data and a response returned before the connection is closed.

Implementing a TCP server requires only a little more effort. First, a TcpListener is created and instructed to "listen" to a local port. Upon accepting an incoming message, a TcpClient is used, as in the TcpClient example, to retrieve the data.

The UDP implementation is included in the UdpClient class. Although this class is simple to use due to its Read and Write methods, this protocol has the negative characteristic of not guaranteeing arrival of packets (or the order of arrival), and therefore, the program has to deal with time-outs, resends, and reassembling of packets. However, one benefit of UDP is that this protocol has some interesting broadcasting services.

Communicating Using Infrared

Infrared ports are included on a variety of devices, including notebook computers and handheld devices. This wireless data-transfer capability is defined by IrDA,[9] which is a group of international companies made up of manufacturers and software companies that promote infrared interoperability.

[9] More information on IrDA can be found at www.irda.org.

The IrDA standards are designed to foster communication on low-cost hardware components that have low power requirements and that easily communicate wirelessly using infrared technology. The purpose of infrared ports is to have a wireless ad hoc data-transfer option, instead of relying on serial cables and serial ports.

IrDA is a short-distance mechanism (up to three feet originally, but results will vary) and was originally designed as half duplex, which means only one endpoint can communicate at a time. The ports should be in a line-of-sight scenario and, therefore, pointed at each other. The IrDA specifications define a protocol stack, which includes connection initialization (handshaking), the frequency of light, device address discovery, data-rate negotiation, information exchange, shutdown, and device address conflict resolution.

In the past, transmission rates have not been blazingly fast, but this story is changing. Implemented under Serial IrDA (SIR or IrDA 1.0), older devices once in the neighborhood of 2.4Kbps now range up to 115.2Kbps half duplex, which is sufficient for simple data transfers. Fast IrDA (FIR or IrDA 1.1) is a specification for full-duplex transfers of up to 4Mbps, which would be acceptable for most applications and is available today. But if high bandwidth is required, keep your eye on Very Fast IrDA (VFIR), which will support up to 16Mbps. Additionally, newer Pocket PC devices such as the iPAQ Pocket PC H3950 also support Consumer IR, which is useful for communicating with consumer electronic devices such as DVD players and televisions and for supporting longer transmission distances.

It is hard to find a Windows CE device that does not have an infrared port. When on a handheld device, there are many application scenarios for infrared data transfer. In most environments, the infrared port can be assigned to a serial port so that control software that understands serial ports only can also support infrared ports.

In the context of programming against these ports, most operating systems provide a socket-based implementation called IrSock, which is built on top of the IrDA stack. The Compact Framework gives a managed interface to this capability. Therefore, terms from the earlier sockets sections will be used, and the code should be recognizable.

Infrared Support in Compact Framework

graphics/key point_icon.gif

The IrDA support in the Compact Framework is for the client side (or the initiating side) of a transfer only. After a developer references the System.Net.Irda.dll assembly in the SDP, the System.Net.Sockets namespace will have several IrDA-related classes brought into the application. This is one of the few areas where the Compact Framework has functionality that the desktop Framework does not (and if a desktop Framework developer wants this ability, then P/Invoke will be required to call the functionality). The Compact Framework's support for IrDA is based on the Information Access Service (IAS) layer of the IrDA specification. Therefore, the managed implementation comes in three sections of classes: listener, client, and discovery.

As the RecvIrData function in Listing 4-7 shows, IrDAListener is used to monitor incoming messages. And, just like the earlier socket and TcpListener examples, a client class is used to receive the incoming message. The client class is called IrDAClient, which maintains a consistent naming pattern. A client application uses IrDAClient to send a message to a server application, as shown in the SendIrData function.

In order to handle discovery of devices, IrDAClient has a method, DiscoverDevices, that returns an array of IrDADeviceInfo objects, as shown in the FindAtomicIrDevice method in Listing 4-7. The IrDADeviceInfo object has several properties, including references to other IrDA classes, such as IrDACharacterSet and IrDAHint. IrDACharacterSet is an enumeration that provides for different character sets, including ASCII, Unicode, Arabic, Cyrillic, Greek, Hebrew, and Latin. IrDAHint is an enumeration that indicates the device type of a remote device. Example values are computer, fax, printer, modem, telephony, and others.

Listing 4-7 IrDA Examples. These methods show the simplicity of working with IrDA.
Imports System.Net
Imports System.Net.Sockets

Private ServiceName As String = "ATOMIC_SOCKET"

Private Sub SendIrData(ByVal buffer() As Byte, _
  ByVal bufferLen As Integer)

  Dim client As IrDAClient = Nothing
  Try
    client = New IrDAClient(ServiceName)
  Catch se As SocketException
    ' Handle exception
  End Try

  Dim stream As System.IO.Stream
  Try
    stream = client.GetStream()
    stream.Write(buffer, 0, bufferLen)
  Finally
    If (Not stream Is Nothing) Then
      stream.Close()
    End If
    If (Not client Is Nothing) Then
      client.Close()
    End If
  End Try
End Sub

'''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Function RecvIrData(ByVal buffer() As Byte, _
    ByVal bufferLen As Integer) As Integer

  Dim bytesRead As Integer = 0
  Dim listener As IrDAListener = New IrDAListener(ServiceName)
  Dim client As IrDAClient = Nothing
  Dim stream As System.IO.Stream = Nothing

  Try
    listener.Start()

    ' Wait for connection
    client = listener.AcceptIrDAClient()
    stream = client.GetStream()
    bytesRead = stream.Read(buffer, 0, bufferLen)

  Catch e as Exception
    ' Handle the exception

  Finally
    If (Not stream Is Nothing) Then
      stream.Close()
    End If
    If (Not client Is Nothing) Then
      client.Close()
    End If
    listener.Stop()
  End Try

  Return bytesRead
End Function

'''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Function FindAtomicIrDevice() As IrDADeviceInfo
  Dim irdaClient As New IrDAClient
  Dim irdaDevices() As IrDADeviceInfo
  Dim oDevice As IrDADeviceInfo

  irdaDevices = irdaClient.DiscoverDevices(3)
  For Each oDevice In irdaDevices
    Dim EndPoint As New IrDAEndPoint(oDevice.DeviceID, "IrDA:IrCOMM")
    irdaClient.Connect(EndPoint)
    If oDevice.DeviceName = "AtomicDevice" Then
      Exit For
    End If
  Next
End Function

One of the quicker ways to get an IrDA example running is to use the tic-tac-toe example provided with VS .NET 2003. Not only is this well implemented, but the game is not bad either.

Other Issues in Network Communications

Many network capabilities and issues have been discussed in this chapter. In this final section, we will cover two more considerations. First, we will show an important utility function that occasionally connected applications can utilize. Second, we will overview Compact Framework support for asynchronous networking functions.

Checking for a Network Connection without Creating a Connection

In some applications, the smart device will often be disconnected from a network. This architecture will require periodic connections so that synchronization activities can be initiated. Most developers would like the application to sense the connection and perform the synchronization in the background. However, the Compact Framework does not provide any events like this yet.

One scheme for implementing this functionality is to check for the existence of a known Web site on a timer event, so that checking for an Internet connection can occur in the background and without the user's knowledge. For cradled device scenarios or devices with a wireless card, this is a satisfactory mechanism. However, this mechanism may not be acceptable for phone-based smart devices, as it will initiate a dial-in to the network every time the code is executed. An application like this would not survive long and would probably cause pain and misery to a support staff.

graphics/key point_icon.gif

Listing 4-8 contains CheckForNetworkConn, which is a handy function developers can package in a common assembly and reuse across projects. It is based on what ActiveSync does when the device is cradled. First, the cradle and the PC become a gateway to the network. Second, ActiveSync assigns an IP address to the device. As a result, whenever the device's address is not 127.0.0.1, the device is talking to the network, and in most cases today, that includes the Internet as well. Another benefit of the code is that it works not only for cradled devices, but for Pocket PC Phones as well because it does not initiate the networking dial-in process, which gives the application a little more control. If your organization is building an application that does periodic connections, you'll find this code useful.

Listing 4-8 Checking for the Existence of a Network Connection.
Imports System.Net

Private Function CheckForNetworkConn() As Boolean
  Dim bRetVal As Boolean = False
  Try
    'returns the Device Name
    Dim HostName As String = Dns.GetHostName()
    Dim thisHost As IPHostEntry = Dns.GetHostByName(HostName)
    Dim thisIpAddr As String = thisHost.AddressList(0).ToString
    bRetVal = thisIpAddr <> IPAddress.Parse("127.0.0.1").ToString()
  Catch ex As Exception
    bRetVal = False
  End Try

  Return bRetVal
End Function

Compact Framework Asynchronous Capabilities in Networking

As mentioned in Chapter 3, there are several asynchronous options available in the desktop Framework that are not available in the Compact Framework. Because this chapter is focused on accessing remote data, Table 4-5 shows the communication methods that have Compact Framework?provided asynchronous support. The list is reflective of the classes covered sequentially in the chapter, except for DNS and NetworkStream.

Table 4-5. Asynchronous Support in Network Communication

Namespace

Class

Synchronous Method

Asynchronous Methods

Your application

XML Web Service proxy class

Any WebMethod

Begin[WebMethod name], End[WebMethod name]

System.Net

WebRequest

GetRequestStream

GetResponse

BeginGetRequestStream, EndGetRequestStream

BeginGetResponse, EndGetResponse

HttpWebRequest

GetRequestStream

GetResponse

BeginGetRequestStream, EndGetRequestStream

BeginGetResponse, EndGetResponse

DNS