G120 - Networking Issues

Hi all,

First, thanks to Gus and GHI for a cool little module in the G120. I picked it up on Friday and despite forgetting that I needed a USB Gadgeteer type power supply/interface, was able to wire it up by hand in just a few minutes on Friday evening. I did burn the latest firmware. Freakin’ Easy as they say…

I did some more playing around over the weekend, trying to cobble together the more relevant information I could find. It seems information is quite scattered…

I got networking up by wiring an ENC28 as described at GHI Electronics – Where Hardware Meets Software and an amalgamation of the code found at “Simple HTTP server using GHI’s Premium Net library” (http://www.tinyclr.com/codeshare/entry/588) and the modifications made by forum member Reinhard Ostermeier (found at http://www.tinyclr.com/forum/topic?id=9396) - thanks, Reinhard.

Basically, the simple web server as coded below works. It starts up, gets an IP address and serves up a page. I am having a couple issues I hope someone can shed some light on…

First, the call to AssignNetworkingStackTo() fails the first time I call it, every single time the program runs. A second call to it, even without delay, works. The solution I put in works but smells. I would rather understand the underlying issue.

Second, pulling the network cable does cause the NetworkAddressChanged delegate to fire but the underlying framework doesn’t appear to handle things well. I get lots of first chance exceptions logged in the debug window, as shown below, and it never recovers connectivity.

Third, has anyone developed a method for avoiding a crash when the web server is hammered by calls (like a DoS)? If I press and hold F5 in my web browser, even for a few seconds, the web server as coded goes unresponsive and never seems to recover.

Thanks again!

Network cable unplugged:

A first chance exception of type 'System.Net.Sockets.SocketException' occurred in Microsoft.SPOT.Net.dll
    #### SocketException ErrorCode = 10050
    #### SocketException ErrorCode = 10050
The thread '<No Name>' (0x4) has exited with code 0 (0x0).
    #### Exception System.NullReferenceException - CLR_E_NULL_REFERENCE (1) ####
    #### Message: 
    #### MFConsoleApplication1.Program::RunServer [IP: 001e] ####
    #### MFConsoleApplication1.Program::Main [IP: 015a] ####
A first chance exception of type 'System.NullReferenceException' occurred in MFConsoleApplication1.exe
    #### Exception System.InvalidOperationException - 0x00000000 (1) ####
    #### Message: 
    #### System.Net.HttpListener::GetContext [IP: 0019] ####
    #### MFConsoleApplication1.Program::RunServer [IP: 0019] ####
    #### MFConsoleApplication1.Program::Main [IP: 015a] ####
A first chance exception of type 'System.InvalidOperationException' occurred in System.Http.dll
    #### Exception System.InvalidOperationException - 0x00000000 (1) ####
    #### Message: 
    #### System.Net.HttpListener::GetContext [IP: 0019] ####
    #### MFConsoleApplication1.Program::RunServer [IP: 0019] ####
    #### MFConsoleApplication1.Program::Main [IP: 015a] ####
A first chance exception of type 'System.InvalidOperationException' occurred in System.Http.dll
    #### Exception System.InvalidOperationException - 0x00000000 (1) ####
    #### Message: 
    #### System.Net.HttpListener::GetContext [IP: 0019] ####
    #### MFConsoleApplication1.Program::RunServer [IP: 0019] ####
    #### MFConsoleApplication1.Program::Main [IP: 015a] ####
A first chance exception of type 'System.InvalidOperationException' occurred in System.Http.dll
    #### Exception System.InvalidOperationException - 0x00000000 (1) ####
    #### Message: 
    #### System.Net.HttpListener::GetContext [IP: 0019] ####
    #### MFConsoleApplication1.Program::RunServer [IP: 0019] ####
    #### MFConsoleApplication1.Program::Main [IP: 015a] ####
A first chance exception of type 'System.InvalidOperationException' occurred in System.Http.dll
    #### Exception System.InvalidOperationException - 0x00000000 (1) ####
    #### Message: 
    #### System.Net.HttpListener::GetContext [IP: 0019] ####
    #### MFConsoleApplication1.Program::RunServer [IP: 0019] ####
    #### MFConsoleApplication1.Program::Main [IP: 015a] ####
A first chance exception of type 'System.InvalidOperationException' occurred in System.Http.dll
    #### Exception System.InvalidOperationException - 0x00000000 (1) ####
    #### Message: 
    #### System.Net.HttpListener::GetContext [IP: 0019] ####
    #### MFConsoleApplication1.Program::RunServer [IP: 0019] ####
    #### MFConsoleApplication1.Program::Main [IP: 015a] ####
A first chance exception of type 'System.InvalidOperationException' occurred in System.Http.dll
    #### Exception System.InvalidOperationException - 0x00000000 (1) ####
    #### Message: 
    #### System.Net.HttpListener::GetContext [IP: 0019] ####
    #### MFConsoleApplication1.Program::RunServer [IP: 0019] ####
    #### MFConsoleApplication1.Program::Main [IP: 015a] ####
A first chance exception of type 'System.InvalidOperationException' occurred in System.Http.dll
    #### Exception System.InvalidOperationException - 0x00000000 (1) ####
    #### Message: 
    #### System.Net.HttpListener::GetContext [IP: 0019] ####
    #### MFConsoleApplication1.Program::RunServer [IP: 0019] ####
    #### MFConsoleApplication1.Program::Main [IP: 015a] ####
A first chance exception of type 'System.InvalidOperationException' occurred in System.Http.dll
    #### Exception System.InvalidOperationException - 0x00000000 (1) ####
    #### Message: 
    #### System.Net.HttpListener::GetContext [IP: 0019] ####
    #### MFConsoleApplication1.Program::RunServer [IP: 0019] ####
    #### MFConsoleApplication1.Program::Main [IP: 015a] ####
A first chance exception of type 'System.InvalidOperationException' occurred in System.Http.dll
    #### Exception System.InvalidOperationException - 0x00000000 (1) ####
    #### Message: 
    #### System.Net.HttpListener::GetContext [IP: 0019] ####
    #### MFConsoleApplication1.Program::RunServer [IP: 0019] ####
    #### MFConsoleApplication1.Program::Main [IP: 015a] ####
A first chance exception of type 'System.InvalidOperationException' occurred in System.Http.dll
Ethernet Cable is Disconneced!
New address for the Ethernet Network Interface 
Is DhCp enabled: True
Is DynamicDnsEnabled enabled: False
NetworkInterfaceType 6
Network settings:
IP Address: 0.0.0.0
Subnet Mask: 0.0.0.0
Default Getway: 0.0.0.0
Number of DNS servers:1
DNS Server 0:208.67.222.222
------------------------------------------------------
    #### Exception System.InvalidOperationException - 0x00000000 (1) ####
    #### Message: 
    #### System.Net.HttpListener::GetContext [IP: 0019] ####
    #### MFConsoleApplication1.Program::RunServer [IP: 0019] ####
    #### MFConsoleApplication1.Program::Main [IP: 015a] ####


… and so on, forever…

My current code:

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

namespace MFConsoleApplication1
{
    public class Program
    {       
        static EthernetENC28J60 Eth1;              

        static byte[] outBuffer;
        static public ManualResetEvent NetworkAvailablityBlocking = null;
        static public ManualResetEvent IPAddressSetResetEvent = null;
        static string myIP = "192.168.1.179";

        static OutputPort ledGreen = new OutputPort(G120.Pin.P0_1, true);

        public static void Main()
        {
            Blink(ref ledGreen, 2);
            Thread.Sleep(500);

            if (Eth1 != null && Eth1.IsActivated)
            {
                Eth1.Close();
                Eth1.Dispose();
            }

            // hardware wired as described in:  http://wiki.tinyclr.com/index.php?title=G120HDR_Developer#How_to_use_ENC28_on_G120HDR_Board
            Eth1 = new EthernetENC28J60(SPI.SPI_module.SPI2, G120.Pin.P1_17, G120.Pin.P0_5, G120.Pin.P1_14);
            

            IPAddressSetResetEvent = new ManualResetEvent(false);
            Eth1.CableConnectivityChanged += new EthernetENC28J60.CableConnectivityChangedEventHandler(Eth1_CableConnectivityChanged);
            Eth1.NetworkAddressChanged += new NetworkInterfaceExtension.NetworkAddressChangedEventHandler(Eth1_NetworkAddressChanged);

                Eth1.Open();
                Thread.Sleep(100);


                //  had to add the try... catch below to make this work.  The call
                //  to AssignNetworkingStackTo() throws an exception the first time
                //  it is called *every time* but the second call works. 
                try
                {
                    NetworkInterfaceExtension.AssignNetworkingStackTo(Eth1);
                }
                catch
                {                    
                    NetworkInterfaceExtension.AssignNetworkingStackTo(Eth1);
                }

                Thread.Sleep(100);


                //Eth1.NetworkInterface.EnableStaticIP(myIP, "255.255.255.0", "192.168.1.1");
                Eth1.NetworkInterface.EnableDhcp();
                Eth1.NetworkInterface.RenewDhcpLease();
               

                if (!Eth1.IsCableConnected)
                {
                    Blink(ref ledGreen, 3);

                    NetworkAvailablityBlocking = new ManualResetEvent(false);

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

               

                while (!IPAddressSetResetEvent.WaitOne(500, false))
                {
                    Debug.Print("IP address is not set yet.");
                }
                Debug.Print("IP address is set.");
                
                
                ledGreen.Write(true);
                string str = Resources.GetString(Resources.StringResources.test);
                outBuffer = Encoding.UTF8.GetBytes(str);

                try
                {
                    Debug.Print("Running HttpServer");
                    Debug.Print("Type this IP address to access the webpage: " + Eth1.NetworkInterface.IPAddress);
                    RunServer();
                }
                catch (Exception e)
                {
                    Debug.Print("Exception thrown by RunServer():  " + e.Message);
                }
            
        }



        private static void Blink(ref OutputPort pin, int p)
        {
            for (int count = 0; count < p; count++)
            {
                pin.Write(true);
                Thread.Sleep(200);
                pin.Write(false);
                Thread.Sleep(200);
            }
        }


        static void Eth1_CableConnectivityChanged(object sender, EthernetENC28J60.CableConnectivityEventArgs e)
        {
            Blink(ref ledGreen, 5);

            Debug.Print("Ethernet Cable is " + (e.IsConnected ? "Connected!" : "Disconneced!"));
            if (e.IsConnected)
            {
                // the call to Set() was throwing an exception.
                // With the null check, it appears the libraries
                // are throwing exceptions internally when the
                // cable is unplugged and they get caught in a
                // loop and never recover.
                if (NetworkAvailablityBlocking != null)
                    NetworkAvailablityBlocking.Set();

                ledGreen.Write(true);
            }
        }


        static void Eth1_NetworkAddressChanged(object sender, EventArgs e)
        {
            //Blink(ref ledGreen, 4);

            Debug.Print("New address for the 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("------------------------------------------------------");
            
            if (Eth1.NetworkInterface.IPAddress == myIP || Eth1.NetworkInterface.IPAddress != "0.0.0.0")
                IPAddressSetResetEvent.Set();
        }

        internal static void RunServer()
        {
            //Blink(ref ledGreen, 5);

            HttpListener listener = new HttpListener("http");
            listener.Start();

            while (true)
            {
                HttpListenerResponse response = null;
                HttpListenerContext context = null;

                try
                {
                    context = listener.GetContext();
                    response = context.Response;
                    HttpListenerRequest request = context.Request;
                    switch (request.HttpMethod.ToUpper())
                    {
                        case "GET": ProcessClientGetRequest(context); break;
                    }

                    if (response != null)
                    {
                        response.Close();
                    }
                }
                catch
                {
                    if (context != null)
                    {
                        context.Close();
                    }                  
                }
            }
        }

        private static void ProcessClientGetRequest(HttpListenerContext context)
        {           
            HttpListenerRequest request = context.Request;
            HttpListenerResponse response = context.Response;

            Debug.Print(request.RawUrl);
            response.OutputStream.Write(outBuffer, 0, outBuffer.Length);

            Blink(ref ledGreen, 2);
        }

    }
}

This is currently a known issue in the G120 firmware. We expect to have this fixed soon.

These are all caused because the webserver is sitting in a while loop, even after you have lost connection with the network. The socket error code 10050 you see in the output is for when the network has unexpectedly gone down.

@ Steven, so I assume the key takeaway is that instead of a WHILE loop, or as part of it, the listener needs to be restarted when this situation arises.

Perhaps set a network connected/not flag in the status changed event and check it in the loop? I haven’t given a lot of thought to how best to address this. But I need to do it because network connection is bye-bye until a reboot. I’ll post something that works once I figure it out.

My only suggestion to this is one of logic. For this logic to work, you will need to accept the new connection in your main loop, and send the socket object to a thread to process. Once the code has been established in such a mannor, you can keep track of the connection amounts and set up a timer to review how many connections are incoming, and if the amount reaches the threshold limit, disable the network for a set period of time. This is an extremely basic implementation, and can be easily overcome, however, something more in depth would leave your actual application little to no room to run on.

You can also simply limit the total number of clients the framework will put on hold with the socket.listen() method of the primary socket.

If you fear your device may be the target of a port-scan, you can research the possibility of implementing ‘Tarpitted’ ports. The idea of a port tar-pit is that it accepts the client connection request on a specified port, but nothing more than the acceptance of the connection is transmitted. This causes the client to believe that it has a true connection, thus avoiding the time-out clause. Simply put, the client will wait forever for a response that will never come.

Security related issues can be very fun, challenging and the main reason you should buy stock in ibuprofen.

I have exactly the same problem with my EMX board. When I disconnect the network cable and plug it back in I never get it working again until a reboot.
I tried detecting the unplug event, catching exception but nothing seems to help.
Does someone have a working sample code that doesn’t have this problem?

Thanks!