Cobra TCP server exploration / tutorial

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.

:clap:

I don’t have a Cobra, but I think that this kind of post could be (is) very useful !

Thanks a lot !

Thank you!! This is the first example which explains TCP with .net in a way I can fully understand it! Please create some sort of small book of it? And please create a project page ;D :clap:

OK, so lets do something useful with this trivial web server.

How about … let’s return the state of the three navigation (up, down and select) buttons on the Cobra.

When you run it, hold down one or more buttons and refresh the page. You will see the page state showing the button states. Of course, instead of buttons, the values could be a temperature reading or the state of an input switch or something.

We have to do three things
[ulist]we need to declare the buttons,
we have to write a little method to build a proper web page, and
we need to call that method to return a long string with the page in it and send it to the browser
[/ulist]

First, the declarations are easy. Note that the buttons short the pin to ground, but they have no pullup resistor on the board. We need to turn the pull-up resistor on in the input port declarations. As they’re buttons, we shjould debounce them, too:

        static InputPort upButton = new InputPort((Cpu.Pin)FEZ_Pin.Digital.ButtonUp, true, Port.ResistorMode.PullUp);
        static InputPort selectButton = new InputPort((Cpu.Pin)FEZ_Pin.Digital.ButtonSelect, true, Port.ResistorMode.PullUp);
        static InputPort downButton = new InputPort((Cpu.Pin)FEZ_Pin.Digital.ButtonDown, true, Port.ResistorMode.PullUp);

Next, we need to make a little method that reads our buttons, build strings to represent their states, and then uses those to build a web page. I’m not going to do an HTML class, but it’s easy.

I’ve only used three elements - … which encloses the whole page, … which encloses the heading (you will see this in the browser’s title bar), and … which enclose the core of the page.

There are two formatting elements, too …

enclose the text of each of three paragraphs, and we enclose the button state strings in to make them italic. All this has no purpose other than to show this really is a fully rendered web page for the non-believers. Here’s the method:
        // Read the states of the Cobra buttons and build a web page showing their states
        static string ButtonPage()
        {
            // Determine the states of the three cobra buttons
            string ubs; if (upButton.Read() == false) ubs = "Pressed"; else ubs = "Released";
            string sbs; if (selectButton.Read() == false) sbs = "Pressed"; else sbs = "Released";
            string dbs; if (downButton.Read() == false) dbs = "Pressed"; else dbs = "Released";

            // Build the web page
            string s = "<html>\n";                                      // First the page type
            s += "<head><title>Fez Cobra Test Page</title></head>\n";   // now the page header
            s += "<body>\n";                                            // start the body        
            s += "<p>Up Button State = <i>" + ubs + "</i></p>";         // Up button, state in italics
            s += "<p>Select Button State = <i>" + sbs + "</i></p>";     // Select button, state in italics
            s += "<p>Down Button State = <i>" + dbs + "</i></p>";       // Down button, state in italics
            s += "</body>";                                             // close the body section
            s += "</html>";                                             // close the page type
            return s;
        }

Finally a small modification to the main loop to call this method. here is the whole program as it now stands. I’ve reduced the bits shown in the first post to outlining. You can see the changed call in the main loop.

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
    {

        static InputPort upButton = new InputPort((Cpu.Pin)FEZ_Pin.Digital.ButtonUp, true, Port.ResistorMode.PullUp);
        static InputPort selectButton = new InputPort((Cpu.Pin)FEZ_Pin.Digital.ButtonSelect, true, Port.ResistorMode.PullUp);
        static InputPort downButton = new InputPort((Cpu.Pin)FEZ_Pin.Digital.ButtonDown, true, Port.ResistorMode.PullUp);

        public static void Main()
        {
            #region Check we have a valid NIC
            // 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();
            }
            #endregion

            // OK, retrieve the network interface
            Microsoft.SPOT.Net.NetworkInformation.NetworkInterface NI = Microsoft.SPOT.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0];

            #region DHCP Code
            // If DHCP is not enabled, then enable it and get an IP address, else renew the lease. Most of 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.
            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

            #region Create and Bind the listening socket
            // 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);
            #endregion

            // 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(ButtonPage());
                    newSock.Send(messageBytes);
                    newSock.Close();
                }
                catch (Exception e)
                {
                    Debug.Print(e.Message);
                }
            }

        }

        // Read the states of the Cobra buttons and build a web page showing their states
        static string ButtonPage()
        {
            // Determine the states of the three cobra buttons
            string ubs; if (upButton.Read() == false) ubs = "Pressed"; else ubs = "Released";
            string sbs; if (selectButton.Read() == false) sbs = "Pressed"; else sbs = "Released";
            string dbs; if (downButton.Read() == false) dbs = "Pressed"; else dbs = "Released";

            // Build the web page
            string s = "<html>\n";                                      // First the page type
            s += "<head><title>Fez Cobra Test Page</title></head>\n";   // now the page header
            s += "<body>\n";                                            // start the body        
            s += "<p>Up Button State = <i>" + ubs + "</i></p>";         // Up button, state in italics
            s += "<p>Select Button State = <i>" + sbs + "</i></p>";     // Select button, state in italics
            s += "<p>Down Button State = <i>" + dbs + "</i></p>";       // Down button, state in italics
            s += "</body>";                                             // close the body section
            s += "</html>";                                             // close the page type
            return s;
        }
    }
}

Finally, Robert & Bec, thanks for the kind words.

Phil

You deserve more than the poor 200 exp points bonus you currently have :wink:

This is very useful but will be lost on about a week :frowning: please make a wiki project page for it and 500 points will be yours :slight_smile:

Give me a few more days. I’d like to explore the process if that’s OK. I’ll make a wiki page once it progresses but I’d like to see what can be done in a blog-like fashion. The wiki pages a re a little static. I’ll be adding to this every day or two.

Yes please, I would hate to see nice posts/tutorial getting lost between thousands of posts :slight_smile:

OK, I surrender. I’ll create a project page for it tonight.

thank you! ;D

I’m sorry, but I’ve spent twice as long trying to get the wiki to show code readably as I did writing the tutorial so far. It’s just too much work to do that there.

I’ll leave it to you guys. I can continue here if you want, but I haven’t got the time to spend messing with wiki formatting.

You do not have to struggle, that is why everyone is here :slight_smile: What is the link to the page you created?

Use:

//your code

One other nice thing about how Wiki works is that you can just look at a page you think looks nice and click on edit to see how its done and then copy that idea over to your page.

I never remember the wiki context! It is much easier to just copy it from another page like Jeff said

I found your page, and fixed it :slight_smile:

Please take a look and fix it anyway you like (link removed)

I am giving you 500 points since many users seem to like what you have.

Also, if others think they can make it better then please go ahead…this is a wiki…anyone can jump in

Thanks for updating the project page for me, Gus. I was really frustrated with it last night, but I think I get it now. I’ll keep working on it tonight.

All fixed. I’ll move this to that page and just post a notice of any updates in this thread.

Thanks for all the wiki help, guys.

I have added a new section to the tutorial. This is a simple trick on how to build an auto-refreshing client page, rather than having to endless hit refresh.

With this change, you connect and you will see a refresh each 1 second (in this case, though you can change that).

The tutorial is here: (link removed)

The next one will be in a couple of days at least - we will talk about multiple client connections … initially within the single thread, and after that we will have a look at how to serve multiple clients on separate threads.

Then we will go on to looking at handing two way interaction with our multiple web clients.

Cheers!

Phil

Nice program to come ! :clap:

Thank you !