Taking some time out from real work, I thought I’d do some messing about with my Cobra and get my head around the IP stack functionality and how it integrates with .NET.
I’ve not presented something in this format before. I’m kind of going to treat it like a blog, so you will see me stuff up as I go. Comments welcome. I have done this a lot before on many different platforms, so I’m no beginner. Later on in the process I’ll take you to some interesting places, but for now, bear with me.
The usual way books teach this is to write a simple TCP server and TCP\ client application. That’s kind of silly in a way, because we all have a client app sitting on our PCs - it’s called a browser!
Do you know what the difference is between a web server and a TCP server? Nothing. A web server is just a TCP server that operates on a defined port and where the client expects a certain test file format to come streaming down (which is why you type http: at the start of an address - to tell your browser what to expect: HTTP marked up text).
So, let’s look at the (functional) bit of code. To run this you will also need to add a reference to Microsoft.SPOT.net to your Cobra project.
using System;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.FEZ;
namespace FEZ_Cobra_TCP_test
{
public class Program
{
public static void Main()
{
// First, make sure we actually have a network interface to work with!
if (Microsoft.SPOT.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces().Length < 1)
{
Debug.Print("No Active network interfaces. Bombing out.");
Thread.CurrentThread.Abort();
}
// OK, retrieve the network interface
Microsoft.SPOT.Net.NetworkInformation.NetworkInterface NI = Microsoft.SPOT.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0];
// If DHCP is not enabled, then enable it and get an IP address, else renew the lease. Most iof us have a DHCP server
// on a network, even at home (in the form of an internet modem or wifi router). If you want to use a static IP
// then comment out the following code in the "DHCP" region and uncomment the code in the "fixed IP" region.
#region DHCP Code
if (NI.IsDhcpEnabled == false)
{
Debug.Print("Enabling DHCP.");
NI.EnableDhcp();
Debug.Print("DCHP - IP Address = " + NI.IPAddress + " ... Net Mask = " + NI.SubnetMask + " ... Gateway = " + NI.GatewayAddress);
}
else
{
Debug.Print("Renewing DHCP lease.");
NI.RenewDhcpLease();
Debug.Print("DCHP - IP Address = " + NI.IPAddress + " ... Net Mask = " + NI.SubnetMask + " ... Gateway = " + NI.GatewayAddress);
}
#endregion
#region Static IP code
// Uncomment the following line if you want to use a static IP address, and comment out the DHCP code region above
//NI.EnableStaticIP("192.169.0.25", "255.255.255.0", "192.169.0.1");
#endregion
// Create the socket
Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// Bind the listening socket to the port
IPAddress hostIP = IPAddress.Parse(NI.IPAddress);
IPEndPoint ep = new IPEndPoint(hostIP, 80);
listenSocket.Bind(ep);
// Start listening
listenSocket.Listen(1);
// Main thread loop
while (true)
{
try
{
Debug.Print("listening...");
Socket newSock = listenSocket.Accept();
Debug.Print("Accepted a connection from " + newSock.RemoteEndPoint.ToString());
byte[] messageBytes = Encoding.UTF8.GetBytes("Hello, browser! I think the time is " + DateTime.Now.ToString());
newSock.Send(messageBytes);
newSock.Close();
}
catch (Exception e)
{
Debug.Print(e.Message);
}
}
}
}
}
So what does all that do?
Well, first we need to make sure that we really have a functional network interface on the board we are running on. that’s done with:
// First, make sure we actually have a network interface to work with!
if (Microsoft.SPOT.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces().Length < 1)
{
Debug.Print("No Active network interfaces. Bombing out.");
Thread.CurrentThread.Abort();
}
[italic]getAllNetworkInterfaces[/italic] returns an array of network interface devices. Note that this is the only way to get an object that points to a network interface. You can’t directly instantiate such an object as the constructor is private. I’m going to assume we’re running a Cobra here and that we want to use the first returned interface.
OK, now we need to get the actual object that manages the first interface. We do that with:
// OK, retrieve the network interface
Microsoft.SPOT.Net.NetworkInformation.NetworkInterface NI = Microsoft.SPOT.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0];
Now, we need an IP address. The Cobra has a default IP (192.168.0.200). Ideally, if your network has a DHCP (Dynamic Host Control Protocol) server on it, we want to use it. That way we can avoid conflicting a manually chosen IP with one already on the network. To do that we need this bit of code:
if (NI.IsDhcpEnabled == false)
{
Debug.Print("Enabling DHCP.");
NI.EnableDhcp();
Debug.Print("DCHP - IP Address = " + NI.IPAddress + " ... Net Mask = " + NI.SubnetMask + " ... Gateway = " + NI.GatewayAddress);
}
else
{
Debug.Print("Renewing DHCP lease.");
NI.RenewDhcpLease();
Debug.Print("DCHP - IP Address = " + NI.IPAddress + " ... Net Mask = " + NI.SubnetMask + " ... Gateway = " + NI.GatewayAddress);
}
This enables DHCP on the network interface (I’m going to call it a NIC from here on it) if it is not already enabled, and then gets an IP. [italic]This will take a few seconds on most networks! Be a little patient![/italic] If DHCP has already been enabled, then the leas will be renewed (in other words, it will get the IP address it had before or a new one, at the DHCP servers’ discretion).
The Cobra is normally defaulted to a fixed IP (DHCP disabled) when it’s powered on. If you redeploy the code or reset the board, however, it will come back as DHCP enabled but forgets its’ leased IP, so you need to renew the lease.
If you want to use a fixed IP, comment out the code in the DHCP region and uncomment the static IP address enable line and replace the values with ones you want to use on your network.
OK, now we have to setup a socket on the NIC. You can think of the NIC as your phone line in your house … Once you get a line to the house that’s great … but you need a socket to plug into to talk! In this case the phone system is more like a PABX, as you can have a different conversation on the phone in each socket. The phone’s extension address is analogous to the port number in an IP socket.
We’re going to use port 80, which happens to be the conventional port used by a web browser.
First, the socket is created with:
Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
And then we bins that socket to a particular IP address and port number, with a particular protocol (internet protocol, or TCP, in this case … I’ll go into more detail on that in another post). here is the little bit of code to do that:
IPAddress hostIP = IPAddress.Parse(NI.IPAddress);
IPEndPoint ep = new IPEndPoint(hostIP, 80);
listenSocket.Bind(ep);
Finally, we listen for devices trying to connect to our server with:
listenSocket.Listen(1);
When a connection request comes in, the TCP stack hands it off to a particular operator. The initial socket stays open to receive further connection requests. Our main loop manages an example process of waiting for a connection, handing it off to a new socket, sending some data and closing the socket out. I want to be able to do that over and over, so that all lives in the main program loop:
Debug.Print("listening...");
Socket newSock = listenSocket.Accept();
Debug.Print("Accepted a connection from " + newSock.RemoteEndPoint.ToString());
byte[] messageBytes = Encoding.UTF8.GetBytes("Hello, browser! I think the time is " + DateTime.Now.ToString());
newSock.Send(messageBytes);
newSock.Close();
[italic]Socket.Accept[/italic] is a blocking call - the main thread will stop and wait until a request comes in. When it does, the incoming connection is handed off to a new, dynamically assigned port. We then encode a simple text string (in UTF8, which is Unicode, so I could put Japanese or any other language I wanted in there … also a convention on web pages) and send the string off to the client. We then close the connection out. and wait for a new connection, which will start the process over again.
If you run this, look for the IP address of the device in the debug window. In my case that will be 192.169.0.12
I then simply type “http://192.169.0.12/” into a browser window, and I should see something like this: Hello, browser! I think the time is 01/01/2009 05:53:16[italic][/italic]
So, we’ve just built a web server. I could serve up a complex page by streaming out a http marked up file instead of my simple line of text, or by sending a more complex sequence of string to construct an http file on the run with some data in it. We’ll see that in the next post.
Note that I’ll remove some of the comments and optional bits in future posts. You will need to understand each bit to follow to the next one.
Cheers!
Phil.
PS … I hope someone finds all this useful. Feel free to comment as desired. there are probably better .NET MF ways to do some of this … as I said, this is like a learning blog in some ways for me.