Main Site Documentation

Gadgeteer Webserver – How to keep it up and running (on FEZ Raptor)


#1

Hello everybody,
after switching from G120HDR to FEZ Raptor I had to learn that prepare using the gadgeteer webserver with the new board. You can find the post here: https://www.ghielectronics.com/community/forum/topic?id=15961 But there are still issues to keep the webserver up and running and I opened this new post.

At the G120HDR board I didn’t get it stable, neither for HttpListener nor for simple socket webserver implementations. That’s why I’m looking forward to the gadgeteer webserver implementation. With the new hardware I can exclude hardware problems.

First the current sample code based on: https://www.ghielectronics.com/community/forum/topic?id=12445:

using System;
using Microsoft.SPOT;
using System.Net;
using System.Text;
using System.Threading;
using GHI.Premium.Net;
using Microsoft.SPOT.Hardware;
using Gadgeteer.Networking;

namespace SimpleHttpServer
{
    public class Program
    {
        static EthernetENC28J60 Eth1 = new EthernetENC28J60(SPI.SPI_module.SPI1, GHI.Hardware.G400.Pin.PA28, GHI.Hardware.G400.Pin.PB1, GHI.Hardware.G400.Pin.PC23, 5000);
        static byte[] outBuffer;
        static public ManualResetEvent NetworkAvailablityBlocking = null;
        static string myIP = "192.168.1.14";

        static Gadgeteer.Networking.WebEvent hello;

        public static void Main()
        {
            Eth1.Open();
            NetworkInterfaceExtension.AssignNetworkingStackTo(Eth1);
            Eth1.CableConnectivityChanged += new EthernetENC28J60.CableConnectivityChangedEventHandler(Eth1_CableConnectivityChanged);
            Eth1.NetworkAddressChanged += new NetworkInterfaceExtension.NetworkAddressChangedEventHandler(Eth1_NetworkAddressChanged);
            Eth1.NetworkInterface.EnableStaticIP(myIP, "255.255.255.0", "192.168.1.1");

            if (!Eth1.IsCableConnected)
            {
                NetworkAvailablityBlocking = new ManualResetEvent(false);

                do
                {
                    if (!Eth1.IsCableConnected)
                    {
                        Debug.Print("Ethernet cable is not connected yet.");
                    }
                    else
                        break;
                }
                while (!NetworkAvailablityBlocking.WaitOne(5000, false));
            }

            while (IPAddress.GetDefaultLocalAddress() == IPAddress.Any)
            {
                Debug.Print("IP address is not set yet.");
            }
            Debug.Print("IP address is set");

            Gadgeteer.Networking.WebServer.StartLocalServer(Eth1.NetworkInterface.IPAddress, 80);
            hello = Gadgeteer.Networking.WebServer.SetupWebEvent("hello");
            hello.WebEventReceived += new WebEvent.ReceivedWebEventHandler(hello_WebEventReceived);
        }

        static void hello_WebEventReceived(string path, WebServer.HttpMethod method, Responder responder)
        {
            Debug.Print("Hello world ");
            responder.Respond("Hello world");
        }

        static void Eth1_CableConnectivityChanged(object sender, EthernetENC28J60.CableConnectivityEventArgs e)
        {
            Debug.Print("Built-in Ethernet Cable is " + (e.IsConnected ? "Connected!" : "Disconneced!"));
            if (e.IsConnected)
                NetworkAvailablityBlocking.Set();
        }

        static void Eth1_NetworkAddressChanged(object sender, EventArgs e)
        {
            Debug.Print("New address for The built-in Ethernet Network Interface ");
            Debug.Print("Is DhCp enabled: " + Eth1.NetworkInterface.IsDhcpEnabled);
            Debug.Print("Is DynamicDnsEnabled enabled: " + Eth1.NetworkInterface.IsDynamicDnsEnabled);
            Debug.Print("NetworkInterfaceType " + Eth1.NetworkInterface.NetworkInterfaceType);
            Debug.Print("Network settings:");
            Debug.Print("IP Address: " + Eth1.NetworkInterface.IPAddress);
            Debug.Print("Subnet Mask: " + Eth1.NetworkInterface.SubnetMask);
            Debug.Print("Default Getway: " + Eth1.NetworkInterface.GatewayAddress);
            Debug.Print("Number of DNS servers:" + Eth1.NetworkInterface.DnsAddresses.Length);
            for (int i = 0; i < Eth1.NetworkInterface.DnsAddresses.Length; i++)
                Debug.Print("DNS Server " + i.ToString() + ":" + Eth1.NetworkInterface.DnsAddresses[i]);
            Debug.Print("------------------------------------------------------");
        }
    }
}

And here are my current issues:

1. Issue: Network Reconnection
My current issue is, that I do not know how to handle a network reconnection. At reconnection the code stops working at
if (e.IsConnected)
NetworkAvailablityBlocking.Set();
with a ‘System.NullReferenceException’.
I do not realy understand what the NetworkAvailablityBlocking stays for and why it becomes null if it is instantiated before.

Does somebody knows how to handle a network reconnection?

2. Unstable - no Response
2a. Starting with root page
If I start the Webserver, I get a response in the browser for the root URL 192.168.1.14. I can do many refresh and I get still a response. For each request I get two such lines in the debug output window:
The thread ‘’ (0x8) has exited with code 0 (0x0).
If I change the URL to 192.168.1.14/hello, I get one response, but then there is a timeout at page refresh, that means no response. The hello_WebEventReceived is not entered.
In this case in the debug output only one such line is created.
But the webserver doesn’t stop working. If I go back to the root, I get a response again. But the sub page is not accessible.
2b. Starting with sub page hello
If the first page I request from the webserver is the sub page there is one response before timeout.

Is there something I’m doing wrong?

Thanks for all replies.


#2

Many improvement were done on the 4.3 networking. We are also aware the enc28 driver could use some improvements. Here is the plan, an sdk ships this week to cover all devices in 4.3. After that we are free to work on any networking issue directly.


#3

you can use the NetworkAvailablityBlocking anywhere in code with

NetworkAvailablityBlocking.WaitOne();

to wait for connection without polling (needs only minimal to non resources for waiting.
I assume that if you get an NullReferenceException on reconnect, that the NetworkAvailablityBlocking was set to null somewhere. You could check for if(NetworkAvailablityBlocking != null) in event handler.

Handling a reconnect the right way can be really challenging, and depends in some cases on what you code looks like.

In fact you might need to start all tcp listeners again on reconnect.
I have invested several days to get it working for some of my connections.
I don’t think that there is a simple way, you need to check every connection and TCP listener if they are still working.


#4

@ Reinhard Ostermeier -

 This was unfamiliar to me; so, I went searching; all I could find were 4.1 references? If it is 4.2, or better yet, 4.3; i would appreciate any ref.s you might have. Quite possible my searching wasn't in-depth/comprehensive.

#5

@ andre.m - :slight_smile:


#6

@ Rheinhard
Sorry, but I do not understand how to handle NetworkAvailablityBlocking.WaitOne()in my code.
What do you mean with

?? The code is public, see above :slight_smile: .

From my point of view, it is a standard scenario which should be supported. I cannot believe that this scenario is raised the first time by me.
Can you explain the reconnect handling in detail? Changing the provided would be quite helpful.


#7

@ Gus
Thanks for the roadmap. I’m looking forward for the next SDK. I agree, the ENC28 could use some improvements :slight_smile: .


#8

What I meant is that handling a reconnect can be really challenging
Using the NetworkAvailablityBlocking event is a very simple way for waiting for the initial connect. It is not that good when you want to handle reconnects.
What you need to do is catching all errors that happen on disconnect, everywhere where you use Sockets, and then reinitialize everything in the CableConnectivityChanged and NetworkAddressChanged events.
My experience is that this all works better in the 4.3 beta firmware than in 4.2.
But I’m comparing older 4.2 with 4.3 here, may be the later 4.2 FW is as good as 4.3 in networking.

What you should do is add some try catch around your network code (more fine graded is better), add debug prints in the network events and the catch blocks, and learn by this how the system reacts on dis- and reconnect.

btw.
You should never rely on the sample code for your own software.
These code snippets are there to show the general functionality.
Usually they are quite bad in error handling, and also in handling special events like a reconnect. (partially because this would make the code harder to read)
These are usually the last 5% of code, that need 50% of the development time, to get it right.


#9

@ Reinhard:
Thanks, and you are right a sample is only sample and exception handling is usually more complex.

@ All
But sometime the world is easy. The ‘System.NullReferenceException’ occurs, because NetworkAvailablityBlocking is instantiated only if the cable is not connected on program start.
If this line of code is moved before to do-while block, you can disconnect and reconnect the cable and the server webserver continues.
So back to point 2.
If I call the hello page it only runs once. Any ideas?

And by the way Point 3 :slight_smile:
Is there a way to define a WebEvent which will be entered if there is no match to the others? I already used wildcards like “*”, but it doesn’t work. The goal would be to enter only on method for /hello and /hello/test1 and /hello/test2.


#10

If you want to use the NetworkAvailablityBlocking for reconnecting, then don’t forget to clear it’s state, when the cable is disconnected.