Determining Whether the Device Is Connected
Most network applications need to know whether the device is currently connected to the network. A well-designed mobile application allows the user to make use of it even if the device isn’t connected. For example, if the device has lost wireless connectivity, it should be able to operate using data cached on the device, and then possibly re-sync with a server when network connectivity is regained.
There is no class in the .NET Compact Framework that raises events to inform an application when connection to the network is lost and regained. Instead, you must code with this eventuality in mind and make sure your application is tolerant of network outages. You might need to write code that polls for a network resource to determine when the device has network connectivity.
For example, if your application uses XML Web services, you might need to connect to the service to upload data changes and download data to the device when a network connection is available. In your application, you should write a method that uses the WebResponse class to send an HTTP GET request to a valid URL on the server where the XML Web service is located. If the HttpWebResponse.StatusCode property in the response equals HttpStatusCode.OK, connectivity to the server is confirmed. You should call this method from a TimerEvent of a System.Threading.Timer instance to poll the server at intervals; when the method succeeds, the application knows that it can make a successful HTTP connection to the server, so the interaction with the XML Web service or access to other network resources can proceed.
Understanding WebRequest and WebResponse Basics
The most important classes in the
System.Net
namespace are the
WebRequest and
WebResponse classes, which are abstract classes.
using System.Net;
using System.IO;
WebRequest req = WebRequest.Create(uri);
WebResponse res = req.GetResponse(); // GetResponse blocks until the response arrives
Stream ReceiveStream = res.GetResponseStream(); // Read the stream into a string
StreamReader sr = new StreamReader( ReceiveStream );
string resultstring = sr.ReadToEnd();
Fetching a Resource Using HTTP
If the scheme of the URI is http:// or https://, the Create method returns an instance of the HttpWebRequestclass. The GetResponse method of this instance makes a request to the resource and returns anHttpWebResponse instance that contains the response.
using System;
using System.IO;
using System.Net;
namespace NETCFDevelopersReference
{
class HttpGetSample
{
///
/// The main entry point for the application.
///
static void Main(string[] args)
{
HttpGetSample thisclass = new HttpGetSample();
thisclass.getPage("target URL here"); // <-- EDIT THIS!
}
public void getPage(String url)
{
WebResponse result = null;
try
{
WebRequest req = WebRequest.Create(url);
result = req.GetResponse();
Stream ReceiveStream = result.GetResponseStream();
//read the stream into a string
StreamReader sr = new StreamReader( ReceiveStream );
string resultstring = sr.ReadToEnd();
Console.WriteLine("\r\nResponse stream received");
Console.WriteLine(resultstring);
}
catch(Exception exp)
{
Console.Write("\r\nRequest failed. Reason:");
Console.WriteLine(exp.Message);
}
finally
{
if ( result != null )
{
result.Close();
}
}
Console.WriteLine("\r\nPress Enter to exit.");
Console.ReadLine();
}
}
}
The Finally block ensures that the WebResponse object is closed, regardless of the success or failure of the program. This is important to ensure that network resources are not wasted.
Handling Errors
You get an exception of type UriFormatException when the URI cannot be parsed or an exception of type WebException for other errors. The Status property of WebException returns a value from the WebExceptionStatus enumeration. If Status is WebExceptionStatus.ProtocolError, the exception is a result of an HTTP protocol error. You can then access the WebException.Response property, which returns an HttpWebResponse object containing the response from the server. Use the HttpWebResponse.StatusCodeproperty to find out the HTTP status code the server sent back, and use HttpWebResponse.StatusDescription to obtain the description string that corresponds to that status code.
Working with HTTP Headers
The HttpWebRequest class allows you to control the HTTP headers sent with your request, which you do by setting properties or by executing methods of the class.
HTTP Headers Sent |
HTTP Header |
Set By |
Accept |
HttpWebRequest.Accept property |
Connection |
HttpWebRequest.Connection and HttpWebRequest.KeepAlive properties |
Content-Length |
HttpWebRequest.ContentLength property |
Expect |
HttpWebRequest.Expect property |
Date |
Set to the current system date |
Host |
Not set |
If-Modified-Since |
HttpWebRequest.IfModifiedSince property |
Range |
HttpWebRequest.AddRange method |
Referer |
HttpWebRequest.Referer property |
Transfer-Encoding |
HttpWebRequest.TransferEncoding property (the HttpWebRequest.SendChunked property must be true) |
User-Agent |
HttpWebRequest.UserAgent property |
The HttpWebResponse instance exposes HTTP headers as properties in a similar way.
HTTP Headers Received |
HTTP Header |
Read By |
Content-Encoding |
HttpWebResponse.ContentEncoding property |
Content-Length |
HttpWebResponse.ContentLength property |
Content-Type |
HttpWebResponse.ContentType property |
Length |
HttpWebResponse.Length property |
Server |
HttpWebResponse.Server property |
Both classes also have a
Headers
property that exposes HTTP headers as a
System.Net.WebHeaderCollection
instance. For example, instead of using the
UserAgent
property, you can add the User-Agent header using the following code:
HttpWebRequest req
=
(HttpWebRequest) WebRequest.Create(uri);
req.Headers.Add(
"
User-Agent
"
,
"
dotNETCF Test Program
"
);
HttpWebResponse res
=
(HttpWebResponse) req.GetResponse();
String server
=
res.Headers[
"
Server
"
];
Network Transfer Encoding
All data transferred over the network is encoded. The Windows CE operating system uses Unicode to represent characters, but this requires at least 2 bytes to represent any character, which is not as efficient as the U.S. ASCII character set, which requires only one byte per character. But ASCII represents only commonly used characters in Western alphabets, so it is unusable in languages requiring other characters, such as European languages, Chinese, Japanese, or Arabic.
The compromise solution is to use character encodings. The most common of these is UTF-8, which is a variable-length multibyte encoding requiring a single byte for 7-bit characters that are in the U.S. ASCII character set and 2 bytes for characters outside it. This results in efficient encodings for Western alphabets and the flexibility to handle non-Latin characters.
The default encoding assumed by the HttpWebRequest and HttpWebResponse classes is UTF-8
You can determine the character set encoding of a response by getting the HttpWebResponse.CharacterSetproperty. However, beware, there is a trick to this! You must set the MediaType property on the request; otherwise, the CharacterSet property on the HttpWebResponse will be blank. Even then, CharacterSet will only return the character set encoding used if the first part of the Content-Type header returned with the response (which is the media type) matches the value you set in the MediaType property of the request.
For example, if you set HttpWebRequest.MediaType to “text/html” before calling HttpWebRequest.GetResponse, and the Content-Type header returned with the response is “text/html; charset=utf-8”, the HttpWebResponse.CharacterSet property will contain the name of the character set encoding (“utf-8”) because the “text/html” media type sent in the first part of the Content-Type header matches the value set in HttpWebRequest.MediaType. If the media types differ, HttpWebResponse.CharacterSet is a null string.
HttpWebRequest req = (HttpWebRequest) WebRequest.Create(url);
// You MUST set the MediaType property on the request, otherwise
// the CharacterSet property on the HttpWebResponse object will be
// blank.
req.MediaType = "text/html";
HttpWebResponse result = (HttpWebResponse) req.GetResponse();
// Note that the HttpWebResponse.ContentType property always
// returns the Content-Type header, which will be something like
// "text/html; charset=utf-8"
string contenttype = result.ContentType;
// Character set encoding may be something like ISO-10646-UCS-2,
// or UTF-8, us-ascii etc
string charsetencoding = result.CharacterSet;
//read the stream using the decoder
Stream ReceiveStream = result.GetResponseStream();
Encoding encode = System.Text.Encoding.GetEncoding(charsetencoding);
StreamReader sr = new StreamReader( ReceiveStream, encode );
Using HTTP GET Query String
In the full .NET Framework, the System.Web.HttpUtility class provides methods to encode and decode URL-encoded data, but this class is not supported in the .NET Compact Framework. You must write your own code to handle URL encoding, such as the example code shown here:
using System;
using System.IO;
namespace NETCFDevelopersReference
{
///
/// Replacement for HttpUtility.UrlEncode
///
public class HttpUtility
{
public static string UrlEncode(string instring)
{
StringReader strRdr = new StringReader(instring);
StringWriter strWtr = new StringWriter();
int charValue = strRdr.Read();
while (charValue != -1)
{
if (((charValue >= 48) && (charValue <= 57)) // 0-9
││((charValue >= 65) && (charValue <= 90)) // A-Z
││((charValue >= 97) && (charValue <= 122))) // a-z
strWtr.Write((char) charValue);
else if (charValue == 32) // Space
strWtr.Write('+');
else
strWtr.Write("%{0:x2}", charValue);
charValue = strRdr.Read();
}
return strWtr.ToString();
}
}
}
Using HTTP POST
To use POST, just set the HttpWebRequest.Method property to “POST” and then proceed as for GET. A typical HTML page shown in a Web browser posts data as key=value pairs, with the data URL-encoded, so your application must format the data in that way if that is what the Web server application expects. If you are simply posting application content or XML, just post it as is.
public void doPost(String url, String payload)
{
WebRequest req = WebRequest.Create(url);
req.Method = "POST";
req.ContentType = "text/plain; charset=utf-8";
// Encode the data
byte[] encodedBytes = Encoding.UTF8.GetBytes(payload);
req.ContentLength = encodedBytes.Length;
// Write encoded data into request stream
Stream requestStream = req.GetRequestStream();
requestStream.Write(encodedBytes, 0, encodedBytes.Length);
requestStream.Close();
WebResponse result = req.GetResponse();
}
Alternatives to Cookies for Session State Support
The .NET Compact Framework implementation of the HttpWebResponse class does not support the Cookiesproperty found in the full .NET Framework implementation.
If you are using an ASP.NET application on the server, you can utilize the ASP.NET support for cookieless clients to track your smart device applications session. You enable this by setting cookieless to “true” in thesessionState configuration in the ASP.NET application’s Web.Config
ASP.NET uses a munged URL to track sessions for cookieless clients, meaning it embeds a session ID into the response URI. For example, if your application makes a request to http://myserver/myserverapp.aspx, the response URI that returns to the client is actually something likehttp://myserver/(1en4l345qq203lr2f0h4xt45)/myserverapp.aspx. To the client, it looks like a server-side redirect. In the client application, this response URI is in the HttpWebResponse.ResponseUri property, so the client application need only use this URI for subsequent requests for the server application to be able to identify those requests as coming from that particular client.
Authenticating Requests with the NetworkCredential Class
WebRequest req
=
WebRequest.Create(url);
NetworkCredential creds
=
new
NetworkCredential(
"
andy
"
,
"
pA55w0rd
"
,
""
);
req.Credentials
=
creds;
WebResponse result
=
req.GetResponse();
Making Network Requests via a Proxy Server
//
Pass Proxy string and bypass local machine
WebProxy myProxy
=
new
WebProxy(
"
http://myproxyserver.mine.com:8080
"
,
true
);
//
If this proxy requires authentication
myProxy.Credentials
=
new
NetworkCredential(
"
loginname
"
,
"
password
"
);
Request.Proxy
=
myProxy;
Asynchronous Web Requests
using System;
using System.IO;
using System.Threading;
namespace NETCFDevelopersReference
{
class HttpGetAsyncSample
{
public static void Main(string[] args)
{
HttpGetAsyncSample thisclass = new HttpGetAsyncSample();
thisclass.Run();
}
private bool isComplete;
public void Run()
{
Console.WriteLine();
Console.WriteLine("Test Program makes HTTP AsyncGet"
+ " to http://www.gotdotnet.com/team/netcf");
Console.WriteLine("Press Enter to continue");
Console.ReadLine();
GetHttpAsync callit = new GetHttpAsync();
// Wire up to handle the complete event
callit.GetComplete += new
GetHttpAsync.GetCompleteHandler(this.GetCompleteHandler);
// Set the flag we will check for completion
isComplete = false;
Console.WriteLine("Making async call");
callit.getPage("http://www.gotdotnet.com/team/netcf");
Console.WriteLine("Main thread writes these dots, "
+ "while async fetch proceeds");
Console.WriteLine("in secondary thread");
int count = 0;
while (!isComplete)
{
Console.Write(".");
if (++count == 80) Console.WriteLine();
Thread.Sleep(500); // Sleep for half a second
}
Console.WriteLine("\r\nResponse Received");
Console.WriteLine("Press Enter to exit");
Console.ReadLine();
return;
}
protected void GetCompleteHandler()
{
// Event handler for get complete
isComplete = true;
}
}
}
using System;
using System.Net;
using System.IO;
using System.Text;
namespace NETCFDevelopersReference
{
// The RequestState class is used to pass data
// across async calls
public class RequestState
{
public HttpWebRequest Request;
public RequestState()
{
Request = null;
}
}
///
/// Class makes asynchronous HTTP Get to remote URL
///
public class GetHttpAsync
{
public delegate void GetCompleteHandler();
public event GetCompleteHandler GetComplete;
public void getPage(String url)
{
try
{
HttpWebRequest req =
(HttpWebRequest) WebRequest.Create(url);
// Create the state object
RequestState rs = new RequestState();
// Add the request into the state
// so it can be passed around
rs.Request = req;
// Issue the async request
req.BeginGetResponse(
new AsyncCallback(this.ResponseCallback), rs);
}
catch(Exception exp)
{
Console.WriteLine("\r\nRequest failed. Reason:");
while (exp != null)
{
Console.WriteLine(exp.Message);
exp = exp.InnerException;
}
}
}
private void ResponseCallback(IAsyncResult ar)
{
// Get the RequestState object from the async result
RequestState rs = (RequestState) ar.AsyncState;
// Get the HttpWebRequest from RequestState
HttpWebRequest req = rs.Request;
// Get the HttpWebResponse object
HttpWebResponse resp =
(HttpWebResponse) req.EndGetResponse(ar);
// Read data from the response stream
Stream responseStream = resp.GetResponseStream();
StreamReader sr =
new StreamReader( responseStream, Encoding.UTF8);
string strContent = sr.ReadToEnd();
// Write out the first 512 characters
Console.WriteLine("Length: {0}", strContent.Length);
Console.WriteLine(strContent.Substring(0,
strContent.Length < 512 ? strContent.Length : 512));
// Raise the completion event
GetComplete();
// Close down the response stream
responseStream.Close();
}
}
}