I use the ethernet module to build a small webserver. Therefore, I set it up using DHCP which works just fine. The device gets an IP Address and I can ping it.
However, if I use a HttpListener like this:
listener = new HttpListener("http", 80);
listener.Start();
listener.GetContext();
then the GetContext()-Method does not return, when I issue a request from the browser. Although, in some cases it works. But I wasn’t able to determine, what causes the server to work. The same behavior occurs when using a socket like this:
var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(new IPEndPoint(System.Net.IPAddress.Parse(IPAddress), 80)); //IPAddress is a string property with the IPAddress. IPAddress.Any doesn't work either
server.Listen(1);
Socket connection = server.Accept();
I have disabled every other task on the device, so currently only the webserver is running. The procedure is like the following:
Register handlers for the NetworkUp and NetworkDown events of the Ethernet_J11D object
Set up Ethernet to use DHCP and renew DHCP lease
In the NetworkUp handler start a new thread with the above code.
It wouldn’t be that confusing if it didn’t work some times. But because of this, I am tempted to believe that the code itself works. Is there anything else I am missing?
A quick question regarding your issue. Is it the case that the initial connections work but after a time they stop responding or is it totally random and sometimes earlier requests fail but later requests succeed all within the same execution of the application?
Do you have a loop around your code to accept the connection? Accept only accepts a single connection.
If I start the application once, it may work or may not. If it works, all subsequent requests will work. If it doesn’t, all subsequent requests won’t work (well, I did not wait forever, but about 20 to 30 minutes).
I suspect it is a problem with the Socket initializer. Maybe, ethernet is not fully initialized when the socket is created. Or the socket listens on a different network device, although there is only one ethernet module.
Yes, there is a loop around the code. I just left it away to focus on the main parts.
@ Nico201 - Having used HttpListener extensively on the desktop I thought I would tryout the .NETMF version. Here is the code I quickly tested with, it has processed over 2000 requests so far from 4 browser instances and going strong. I did this on a Spider, maybe you can try this code and see if it works for you.
Note: I used the Ethernet J11D module.
using System;
using System.Threading;
using Microsoft.SPOT;
using GTM = Gadgeteer.Modules;
using System.Net;
using System.Text;
using System.Net.Sockets;
namespace WebServer
{
public partial class Program
{
void ProgramStarted()
{
// Setup ethernet on the device
ethernet.UseStaticIP("192.168.0.101", "255.255.255.0", "192.168.0.1");
ethernet.NetworkUp += new GTM.Module.NetworkModule.NetworkEventHandler(ethernet_NetworkUp);
}
void ethernet_NetworkUp(GTM.Module.NetworkModule sender, GTM.Module.NetworkModule.NetworkState state)
{
Debug.Print("Network up");
// Start a new thread that will listen for incomming HTTP requests
new Thread(() =>
{
while (true)
{
HttpListener listener = new HttpListener("http", 80);
listener.Start();
Debug.Print("HTTP Listener Started");
while (listener.IsListening)
{
try
{
var context = listener.GetContext();
if (context != null)
{
HandleRequest(context);
}
}
catch (Exception ex)
{
Debug.Print(ex.ToString());
}
}
listener.Stop();
listener.Close();
Debug.Print("Listener stopped listening");
// Reboot here to restart the server if for some reason it
// no longer handles requests
}
}).Start();
}
private static int _requestCounter = 0;
private static string _htmlBeginTemplate = @ "
<html>
<head>
<meta http-equiv=""refresh"" content=""1"">
</head>
<body>";
static string _htmlEndTemplate = @ "
</body>
</html>";
private static void HandleRequest(HttpListenerContext context)
{
new Thread(() =>
{
try
{
context.Response.KeepAlive = true;
_requestCounter++;
string html =
_htmlBeginTemplate +
"Counter = " +
_requestCounter.ToString() + _htmlEndTemplate;
byte[] htmlBytes = Encoding.UTF8.GetBytes(html);
context.Response.ContentLength64 = htmlBytes.Length;
context.Response.OutputStream.Write(htmlBytes, 0, htmlBytes.Length);
}
catch (SocketException socketEx)
{
if (socketEx.ErrorCode == 10035 || socketEx.ErrorCode == 10054)
{
// TCP Connection to client was closed unexepctedly
}
else
{
Debug.Print(socketEx.ToString());
}
}
catch (Exception ex)
{
Debug.Print(ex.ToString());
}
finally
{
context.Response.Close();
}
}).Start();
}
}
}
@ taylorza - Interesting. I was just about to respond that your code looks very similar to mine and that the same errors occur. However, I made a little change. I used DHCP instead of a static IP. But then I decided to test it with the static IP and it works. If I use a static IP in my application, it works as well. Maybe the times when it worked were configured with a static IP. I do not remember exactly. This could be considered kind of a solution. However, using DHCP would be much more convenient and elegant. I wonder, why this causes the Sockets not to respond.
@ Architect - My code is similar to taylorza’s code. And since it shares the same problems, it can be used as well. The only change I made is using DHCP:
Thanks for the link. I already found a whole bunch of webservers, but all of them shared the same problem. Probably because of an issue within the DHCP code.
I suspect that the DHCP netmf code isn’t the problem, it’s the way you’re using it (or most likely, not waiting for the address to be resolved before trying to bind to it). Can you show us your DHCP initialisation code?
Edit: you don’t need to renew the lease at the point you enable DHCP.
OK, another thing in your code that I find strange is your use of the handler for the network event, but you’re assuming the network up event you hit is actually a network up event. What if it’s a network down event? You need to bring your check for network status back into that method, and only start a new thread when the network is actually up. And when the network goes down, you’ll need to end the thread.
@ Brett - Yes, you’re right. I ommitted the NetworkDown event handler, where the thread is stopped.
And yes, it works without the RenewDhcpLease(). Thanks for that.
You enable DHCP. That call works. The network event fires, and your handler runs. You start the thread for your listener.
Then the DHCP Renew Lease call happens, and the network even fires, and your handler runs, and you start another thread for your listener (or some other weirdness).
Perhaps you could only start a thread if one isn’t already running. Maintain a thread object as a global object, and check if it is NULL before starting the thread. And use debug.print to see how many times your call is made, to see if the suspicion above is true or not !