Potential Issue with W5100 Support

I installed the Beta SDK on my PC and update the firmware on a Domino.

I copied the http server program post earlier in the day, tweeked the addresses, and tested the running serverwith IE8.

It works! I was so amazed how easy it was to get working that I took an Arduino out of my parts box and smashed it with a hammer. ???

I tested the program by doing browser refresh each time the prior request was complete. I was trying to see if there was any issue with delayed closing of sockets. Nope… worked fine.

I then decided to try to stress out the server. I began to press the refesh button on the browser as fast as I could. After four or five rapid refreshs I received an exception. I was running with the debugger.

    #### Exception System.Exception - 0x00000000 (25) ####
    #### Message: Socket is closed
    #### GHIElectronics.NETMF.Net.SocketNative::send [IP: 0061] ####
    #### GHIElectronics.NETMF.Net.Sockets.Socket::Send [IP: 0029] ####
    #### GHIElectronics.NETMF.Net.Sockets.Socket::Send [IP: 0010] ####
    #### TCP_HTTP_Server_Test.MySocketServer+ProcessClientRequest::ProcessRequest [IP: 005f] ####
A first chance exception of type 'System.Exception' occurred in GHIElectronics.NETMF.W5100.dll
An unhandled exception of type 'System.Exception' occurred in GHIElectronics.NETMF.W5100.dll

The line of code where the exception occurred:

m_clientSocket.Send(Encoding.UTF8.GetBytes(s));

If I catch and ignore the exception the system recovers in about 3-5 seconds without rebooting.

This is normal :slight_smile: there is a limit of 4 sockets and your test opens sockets too fast so it will go over 4. You can test stressing by sending a lot of data to multiple sockets but leave the sockets open. Browsers close sockets after transfer is complete

Perfect,
This is how it should work. I will explain what happened.

When you requested the page first time your IE sends the Http request and a new thread on your FEZ was created to send the response.But before the FEZ sent the response, you pressed F5 to refresh (stress testing) and your IE send termination request before getting the page. Then the Wiznet chips terminated the connection and closed the socket. Then in your code the thread is trying to send the page, but guess what?! the socket is closed. 8)

In the example code you notice the the program does not exit because of the exception. It only exits the thread. so the server is still working.

Josef:

Thanks for the clear explanation.

Usually I ignore send exceptions since I usually have an outstanding read on the socket and I handle disconnects on read exceptions.

I was not expecting a socket closed exception on the send, but since I know what I means I can sleep well tonight. :smiley:

Please Mike provide us with any comment or any change suggestion that you think has to be made.

It is very important for us to have an easy and flexible WIZnet driver interface for users.

Thanks for the info, Josef! :slight_smile:

Gus:

There are two aspects to providing W5100 support in the “traditional” .NETMF manner; API and error handling.

The API seems to have been handled. I am not sure about the error handling.

I would have expected a SocketException to have occurred on the Send() raher than a SystemException. Further, I would have expected that the Socket would have not been closed, just unconnected.

But, having the stack in the W5100 makes fully compliant traditional error handling difficult if not impossible.

I think an analysis of the difference in error conditions might be helpful in the documentation.

Handling error can be enhanced easily. We are trying to prefect all features first.

This is why we need your suggestions and comments :slight_smile:

After trying to do networking in the Arduino world, with the ethernet shield, having it work is a vast improvement. :slight_smile:

I will be do some more stress testing over the next few days.

You are not paying anything extra fro this but you are getting a professional and commercial support from us :slight_smile: This is not a sample driver but very hard work that we did for long weeks or even months :slight_smile:

…and, as always, if you have any problem, we are here to resolve in extreme short time :wink:

This is done internally by WIZnet chip, nothing we can do. I believe that the IE requests the session termination in that case.

I wrote a small echo server for the W5100. The server accepts up to four sessions, and echoes back all the characters received.

The operation of the Receive() method is not what I expected.

I expected the Receive() method to be blocking. When called, it should return any data that is available in the internal socket buffer, or wait until some data arrives or the receive timeout expires. If the remote end disconnects, the Receive() method would return a zero bytes read count.

The W5100 implementation is non-blocking. If there is no data in the internal buffer then it returns immediately with a zero length, even when the receive timeout is set to -1, which means an infinite receive timeout.

Normally I would run the code on my Cobra to see if I get the same results, but it does not have the beta firmware loaded yet.

I did check some working code I did for the Cobra, which uses the Receive() method, and the code shows that the Receive() method was blocking on the Cobra. I did not use the Poll() method.

The MS examples for sockets seem to use the poll method, which provides the blocking, prior to the Receive().

Bottom line… The Receive() for the W5100 support does not match the Cobra and standard .NET operation.

I don’t know if this is problem with the implementation, or it must be this way because the stack is located on the W5100 chip. If this is the way it has to be with the W5100, then the documentation needs to be updated since it is a major variance from “standards”.

Currently the timeout (blocking ) is implemented in Poll() would this method hepl you waiting for receiving data before runing the receive()

It is late, and I may be missing something, but it seems that something is funny with the Poll method.

Here is the code for an echo server:

u

sing System;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.Net;
using GHIElectronics.NETMF.Net.Sockets;
using GHIElectronics.NETMF.Net.NetworkInformation;
using System.Text;
//using Socket = GHIElectronics.NETMF.Net.Sockets.Socket;

namespace EchoServer
{
    public class EchoServer
    {
        private static int sessionCount = 0;
        private const int MAX_SESSIONS = 4;
        private static Socket newSessionSocket = null;

        public static void Main()
        {
            
            const Int32 c_port = 80;
            byte[] ip = { 192, 168, 1, 201 };
            byte[] subnet = { 255, 255, 255, 0 };
            byte[] gateway = { 192, 168, 1, 1 };
            byte[] mac = { 43, 185, 44, 2, 206, 127 };
            WIZnet_W5100.Enable(SPI.SPI_module.SPI1, (Cpu.Pin)FEZ_Pin.Digital.Di10, (Cpu.Pin)FEZ_Pin.Digital.Di9);
            NetworkInterface.EnableStaticIP(ip, subnet, gateway, mac);
            NetworkInterface.EnableStaticDns(new byte[] { 192, 168, 1, 1 });
            Socket server = new Socket(AddressFamily.InterNetwork,
            SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, c_port);
            server.Bind(localEndPoint);
            server.Listen(1);

            Debug.Print("*** Echo Server Listening");

            while (true)
            {
                // check for four running sessions
                if (sessionCount < MAX_SESSIONS)
                {

                    // Wait for a client to connect.
                    newSessionSocket = server.Accept();
                    Debug.Print("New Sesion");

                    Thread newSession = new Thread(new ThreadStart(SessionThread));
                    newSession.Start();
                    Thread.Sleep(0);
                    Interlocked.Increment(ref sessionCount);       
                }
                else
                {
                    Thread.Sleep(250);
                }
            }
        }

        static private void SessionThread()
        {
            byte[] buffer = new byte[1024];
            Socket socket = newSessionSocket;
            socket.ReceiveTimeout = -1;

            while (true)
            {
                try
                {
                    if (socket.Poll(-1, SelectMode.SelectRead))
                    {
                        int bytesToRead = socket.Available;
                        Debug.Print("bytesToRead: " + bytesToRead.ToString());
                        if (bytesToRead == 0)
                        {
                            throw new Exception("Remote Disconnect");
                        }
                        else
                        {
                            int bytesRead = socket.Receive(buffer, bytesToRead, SocketFlags.None);
                            socket.Send(buffer, bytesRead, SocketFlags.None);
                        }
                    }
                    else
                    {
                        Debug.Print("Poll False");
                    }
                }
                catch (Exception ex)
                {
                    socket.Close();
                    Interlocked.Decrement(ref sessionCount);
                    break;
                }
            }
        }
    }
}

The server is listening on port 80. I used the MS telnet program from the command line and did a connect to the server. (telnet 192.168.1.201 80).

The connection is accepted. After a few seconds, I type a character. The character is received at the server, and echoed. It appears back at the PC.

When the code then issues the poll again, the poll method return with a true and when the Available property is checked it shows zero bytes are available, which is a remote hangup indication.

The server then closes the socket.

The telnet program then says the remote host has disconnected. The old finger pointing problem :slight_smile:

After receiving the first byte, and sending it back, I did not expect the poll method to return true with a zero data available property on the second call to it.

I normally never use the poll method, but I am using it now to get around the Receive() blocking question. I might not understand its use.

[quote]Currently the timeout (blocking ) is implemented in Poll() would this method hepl you waiting for receiving data before runing the receive()
[/quote]

The current Receive() implementation is non-standard. To make it compliant, the implementation just needs to issue a poll internally.

I have never used the poll method while working with .NET sockets. I guess it is there for old BSD programmers. ::slight_smile:

I believe that the W5100 support features should operate as close to the Cobra implementation of the same feature as possible. The only excuse for a variance is the W5100 makes it impossible to do it the same.

Consistant blocking characteristics are important.

:smiley: :smiley: :smiley:

In a prior post I explained a problem I was having using the Poll() method.

I just finished updating my Cobra to the beta SDK.

After updating, I created a new Cobra project and copied the echo server to the new project. I then removed the W5100 required references, added the reference required for Cobra sockets support, and removed the W5100 specified code.

Following is the Cobra echo server code:

using System;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using System.Net.Sockets;
using System.Net;

using GHIElectronics.NETMF.FEZ;
using System.Text;
//using Socket = GHIElectronics.NETMF.Net.Sockets.Socket;

namespace EchoServer
{
    public class EchoServer
    {
        private static int sessionCount = 0;
        private const int MAX_SESSIONS = 4;
        private static Socket newSessionSocket = null;

        public static void Main()
        {

            const Int32 c_port = 80;

            Socket server = new Socket(AddressFamily.InterNetwork,
            SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, c_port);
            server.Bind(localEndPoint);
            server.Listen(1);

            Debug.Print("*** Echo Server Listening");

            while (true)
            {
                // check for four running sessions
                if (sessionCount < MAX_SESSIONS)
                {

                    // Wait for a client to connect.
                    newSessionSocket = server.Accept();
                    Debug.Print("New Sesion");

                    Thread newSession = new Thread(new ThreadStart(SessionThread));
                    newSession.Start();
                    Thread.Sleep(0);
                    Interlocked.Increment(ref sessionCount);
                }
                else
                {
                    Thread.Sleep(250);
                }
            }
        }

        static private void SessionThread()
        {
            byte[] buffer = new byte[1024];
            Socket socket = newSessionSocket;
            socket.ReceiveTimeout = -1;

            while (true)
            {
                try
                {
                    if (socket.Poll(-1, SelectMode.SelectRead))
                    {
                        int bytesToRead = socket.Available;
                        Debug.Print("bytesToRead: " + bytesToRead.ToString());
                        if (bytesToRead == 0)
                        {
                            throw new Exception("Remote Disconnect");
                        }
                        else
                        {
                            int bytesRead = socket.Receive(buffer, bytesToRead, SocketFlags.None);
                            socket.Send(buffer, bytesRead, SocketFlags.None);
                        }
                    }
                    else
                    {
                        Debug.Print("Poll False");
                    }
                }
                catch (Exception ex)
                {
                    socket.Close();
                    Interlocked.Decrement(ref sessionCount);
                    break;
                }
            }
        }
    }
}

The echo server works on the Cobra, with no problem doing the second read poll.

[quote]When the code then issues the poll again, the poll method return with a true and when the Available property is checked it shows zero bytes are available, which is a remote hangup indication.

The server then closes the socket.

The telnet program then says the remote host has disconnected. The old finger pointing problem [/quote]

Issue was identified and fixed.

Will do your suggestion

Thanks Mike, You are a great support. 8)

Josef:

Great!

When will the next spin of the beta sdk be available?

I can not get Dns to work with the W5100.

My test program:

using System;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.Net;
using GHIElectronics.NETMF.Net.Sockets;
using GHIElectronics.NETMF.Net.NetworkInformation;
using System.Text;

namespace DNSTest
{
    public class DnsTest
    {
        public static void Main()
        {
            byte[] ip = { 192, 168, 1, 201 };
            byte[] subnet = { 255, 255, 255, 0 };
            byte[] gateway = { 192, 168, 1, 1 };
            byte[] mac = { 43, 185, 44, 2, 206, 127 };
            WIZnet_W5100.Enable(SPI.SPI_module.SPI1, (Cpu.Pin)FEZ_Pin.Digital.Di10, (Cpu.Pin)FEZ_Pin.Digital.Di9);
            NetworkInterface.EnableStaticIP(ip, subnet, gateway, mac);
            NetworkInterface.EnableStaticDns(new byte[] { 192, 168, 1, 1 });

            //IPHostEntry hostEntry1 = Dns.GetHostEntry("nist1-ny.ustiming.org");
            IPHostEntry hostEntry2 = Dns.GetHostEntry("192.168.1.28");


            Thread.Sleep(Timeout.Infinite);
        }

    }
}

Both forms of the ip address string result in the following exception:

Exception System.ArgumentOutOfRangeException - CLR_E_OUT_OF_RANGE (1)

#### Message: 
#### System.String::get_Chars [IP: 0000] ####
#### GHIElectronics.NETMF.Net.Dns::inet_addr [IP: 0041] ####
#### GHIElectronics.NETMF.Net.Dns::GetHostEntry [IP: 000a] ####
#### DNSTest.DnsTest::Main [IP: 0061] ####

A first chance exception of type ‘System.ArgumentOutOfRangeException’ occurred in mscorlib.dll
An unhandled exception of type ‘System.ArgumentOutOfRangeException’ occurred in mscorlib.dll

The following program works on the Cobra:

using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using System.Net;
using System.Text;

namespace DNSTest
{
    public class DnsTest
    {
        public static void Main()
        {

            IPHostEntry hostEntry1 = Dns.GetHostEntry("nist1-ny.ustiming.org");
            IPHostEntry hostEntry2 = Dns.GetHostEntry("192.168.1.28");

            Thread.Sleep(Timeout.Infinite);
        }

    }
}

Mike

I tried this and find that the DNS lookup fails with

  #### Exception System.IndexOutOfRangeException - 0xa9000000 (1) ####
    #### Message: 
    #### GHIElectronics.NETMF.Net.SocketNative::socket [IP: 002a] ####
    #### GHIElectronics.NETMF.Net.Sockets.Socket::.ctor [IP: 002a] ####
    #### GHIElectronics.NETMF.Net.Dns::GetHostEntry [IP: 004c] ####
    #### FEZ_Panda_Application1.MySocketServer::Main [IP: 0073] ####
A first chance exception of type 'System.IndexOutOfRangeException' occurred in GHIElectronics.NETMF.W5100.dll
An unhandled exception of type 'System.IndexOutOfRangeException' occurred in GHIElectronics.NETMF.W5100.dll