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.
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.
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 |
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.
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.
|
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.
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.
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.
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.
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()
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. |
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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;" & _ "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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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. |
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
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.