Introduction
The following article has two purposes:
- From the architectural perspective: To show an approach to integrate Windows applications with smarthphone
and tablet applications through HTML5 WebSockets.
- From the software engineering perspective: To show a concrete and useful case of
a Visual Studio remote debugging
console on iPhones, extensible to Android devices.
It is not a goal of this article to provide a final and polished solution, the source code and applications provided are
just for demonstrating the concept.
Although the .net application uses the WPF library, it can be used with any UI library.
Background
Few days ago I got an iPhone 4. I was hoping to find a way to use it as a debugging console for my .net applications: No luck.
So, I started to research about the simplest solution to perform this, using the following criteria:
- Avoid to learn a totally new language, like Objective-C
- Avoid to distribute the application through Apple's AppStore (and save a hundred
bucks!)
- Use a firewall-friendly communication technique
- Use a simple library, prefereably just one DLL
The research process was not too long, HTML5 seems to be the best choice for writing an application without requiring the
AppStore process, iOS supports nicely the HTML5 Application Cache feature
and the HTML5 WebSockets complied with the firewall-friendly requirement and was
implemented natively in iOS' Safari browser.
Architecture
In the Windows side, the core functionality of the tool is a TraceListener
-derived class.
This class is responsible to trap the System.Diagnostics.Debug.Write()
and .WriteLine()
calls and sent it to the remote device by using an HTML5 WebSocket server.
On the other hand, the iPhone implements a javascript application using a WebSocket client. The high-level design looks like this:
Implementation
Windows side
In the Windows side,
WebSockets is still not supported natively by .Net (but already announced for the .Net Framework 4.5). After
evaluating a half dozen of C#
WebSocket libraries, I went for
Alchemy (see
http://alchemywebsockets.net ) because of its simplicity
and monolitic DLL implementation.
Another important feature of Alchemy is its support for several versions of the WebSocket protocol. Since it is not a mature standard,
there are many versions of it, depending on the browser brand, version and platform. The supported versions are
hixie-76 (hybi00),
hybi-10, hybi-17 and RFC6455.
As mentioned before, the main class in the windows side is a
TraceListener
-derived class, named
WebSocketTraceListener
.
The implementation is really short, as follows:
namespace System.Diagnostics
{
public class WebSocketTraceListener : TraceListener
{
private Alchemy.WebSocketServer server = new Alchemy.WebSocketServer();
private Alchemy.Classes.UserContext connection = null;
public void Initialize(System.Net.IPAddress ip, int port)
{
try
{
server.ListenAddress = ip;
server.Port = port;
server.OnConnect = OnConnect;
server.OnDisconnect = OnDisconnect;
server.Start();
Debug.WriteLine("WebSocket Trace Listener: An error ocurred when the application tried to start");
Debug.Listeners.Add(this);
}
catch (Exception)
{
Debug.WriteLine("WebSocket Trace Listener: Started correctly.");
}
}
public override void Write(string message)
{
if (this.connection != null)
this.connection.Send(message);
}
public override void WriteLine(string message)
{
if (this.connection != null)
this.connection.Send(message);
}
private void OnConnect(Alchemy.Classes.UserContext context)
{
if (this.connection != null)
this.server.Restart();
this.connection = context;
}
private void OnDisconnect(Alchemy.Classes.UserContext context)
{
this.connection = null;
}
}
}
This class can be embedded directly into your application; there is no need to keep it in a separated DLL. There are two dependencies, though:
The Alchemy library (Alchemy.dll, included in the solution), and System.Web.dll, which requires the project to be configured with
the regular .Net Framework (and not the .Net Framework Client Profile) in order to allow you to attach that DLL. Finally, the
listener class can be used by adding few lines of code:
var listener = new WebSocketTraceListener();
listener.Initialize(IPAddress.Parse("192.168.0.101"), 81);
...
Debug.WriteLine("This is a debug line");
Notice that the IP address belongs to the computer where the debugged application runs, and the port number should be one that
is not blocked by your current OS and Hardware firewall settings; 81 is a good one. The IP address will be usually local, as it is expected
to be connected from the mobile device inside a LAN context. In order to debug an application which is running in Internet, more settings
may be required in your router.
A mockup WPF application has been created in order to send some debug strings according to the user interaction:
Mobile device side
In the mobile device, the source code is simple as well, by using a native WebSocket client object, as in the following template:
var socket;
function Initialize(address) {
socket = new WebSocket(address);
socket.onopen = function (event) {
};
socket.onclose = function(event) {
};
socket.onerror = function (event) {
};
socket.onmessage = function (event) {
};
}
function Stop() {
if (socket !== undefined) {
socket.close();
}
}
The address must indicate the ws:// protocol and shall match the IP and port specified in the Windows side.
Regarding the concrete tool, it has a minimalistic user interface. The first row allows to specify the socket address, start
and stop the connection. The incoming lines will appear from top to bottom and can de scrolled up and down as usual:
When a connection is succesful, the Connect button will become disabled and the Stop button will turn enabled. After stopping
successfully, both buttons will swap their states.
Deployment
The unique file required to be deployed is DebugConsole.htm, which contains all the HTML, CSS and Javascript code. It can be
placed in any server. As this is an HTML5 application using WebSocket, the application will run as a standalone application after
it is loaded through a browser. In order to convert this Internet page into a cached application, there are several tricks and
tips required.
The first step to prepare a cached application is to create a manifest file. The minimum content would be something like:
CACHE MANIFEST
CACHE:
DebugConsole.htm
The web page is linked by specifying the manifest file into the html tag as: <html manifest="manifest_file">
. This will keep the
page cached into the local machine until the manifest is changed at server or the local cache is manually cleared by the user.
The big tip here is that the manifest must be served with MIME type text/cache-manifest
. There are many authors that suggest
to configure the server to allow this new MIME type for the specific file, but sometimes we don't have that degree of control. Another
way to accomplish the same task, is to manually manipulate the headers while creating the manifest file dinamically.
The sample source code shows a way to do it with PHP, but it can be done with ASP as well:
<?php ob_start(); header('Content-type: text/cache-manifest');
echo 'CACHE MANIFEST
CACHE:
DebugConsole.htm';
ob_end_flush(); ?>
A final trick can avoid hours of pain: When alterating the headers at runtime, it is extremely important to not to output
any single character before the header is changed. That would throw a server exception. Be sure to remove any extra white spaces at
the end of the scripted file.
iPhone specifics
There are few extra steps to make this a true iPhone application. We need a couple of resources: a 57x57 icon and a 320x460 splash
screen. Both image files can be png or jpeg. After creating the required resource files, they shall be added to the manifest:
<?php ob_start(); header('Content-type: text/cache-manifest');
echo 'CACHE MANIFEST
CACHE:
Debug_Icon.png
Debug_Start.png
DebugConsole.htm';
ob_end_flush(); ?>
In the HTML file, some META
and LINK
records are needed to tell iOS how to set some behaviors and deal with the resources added:
<head>
<title>VS Debug</title>
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="apple-touch-icon" href="Debug_Icon.png"/>
<link rel="apple-touch-startup-image" href="Debug_Start.png" media="screen and (min-device-width: 200px) and (max-device-width: 320) and (orientation:portrait)" />
The final steps can be seen in the following screen shots. In summary, you have to browse to the desired page by using Safari
and then add the page to the Home Screen. The iOS will scan the manifest and take the icon and splash screen for future use.
And that's it! You are now able to develop more complex iPhone/HTML5 Apps connected to other Window applications.
Live Demo
I have temporarily published a live demo of the debug application at the address: http://www.jaimeolivares.com/debug/DebugConsole.htm, but it could be removed or relocated at any time.