2

Closed

WebServer.StopLocalServer() does not close underlying socket

description

There appears to be a bug where calling WebServer.StopLocalServer() and later calling WebServer.StartLocalServer(...) with the same IP address and port number causes the following exception. This is a common use case (e.g. disconnecting the network cable). One should call StopLocalServer() when the network goes down and then StartLocalServer when the network comes back up. Unfortunately, that fails unless the following code change is made.
#### Exception System.Net.Sockets.SocketException - CLR_E_FAIL (4) ####
#### Message: 
#### Microsoft.SPOT.Net.SocketNative::bind [IP: 0000] ####
#### System.Net.Sockets.Socket::Bind [IP: 0016] ####
#### Gadgeteer.Networking.WebServerManager+Server::StartLocal [IP: 002e] ####
#### Gadgeteer.Networking.WebServerManager::StartServer [IP: 0013] ####
#### Gadgeteer.Networking.WebServer::StartLocalServer [IP: 0015] ####
#### DrySpace.Program::NetworkChange_NetworkAddressChanged [IP: 0058] ####
#### Microsoft.SPOT.Net.NetworkInformation.NetworkAddressChangedEventHandler::Invoke [IP: 266ead70] ####
#### Microsoft.SPOT.Net.NetworkInformation.NetworkChange::OnNetworkChangeCallback [IP: 0040] ####
#### Microsoft.SPOT.Net.NetworkInformation.NetworkChange+NetworkChangeListener::OnEvent [IP: 000d] ####
#### Microsoft.SPOT.EventSink::EventDispatchCallback [IP: 0014] ####
#### SocketException ErrorCode = 10048
A first chance exception of type 'System.Net.Sockets.SocketException' occurred in Microsoft.SPOT.Net.dll
#### SocketException ErrorCode = 10048
#### SocketException ErrorCode = 10048
#### SocketException ErrorCode = 10048
I wrote the text in the <Ignore /> section below before I realized it doesn't solve the problem. I left this section here to provide context to my comments on this issue (which aren't editable).

<Ignore>
This will be verbose so that search engines can more easily pick this up.

In WebServer43.csproj, the project used to build Gadgeteer.WebServer.dll, there appears to be a bug. In the Gadgeteer.Networking namespace, the namespace which contains the WebServer class, is the WebServer.StopLocalServer() method. This method calls WebServerManager.StopAll(). WebServerManager.StopAll() calls Server.Stop(); The bug appears to be that Server.Stop() never calls Socket.Close(). To fix this bug, change the Server.Stop() method in WebServerManager.cs to the following.
public void Stop()
{
     if (LocalServer != null)
     {
          LocalServer.Close();
     }
     IsRunning = false;
}
</Ignore>


While you've got the code up, make the code change referenced here. You'll need that too. See the following link for more discussion on that issue.

Finally, for anyone coming behind me before this actually gets fixed, refer to here [Reply #2] for information on how to get your newly fixed and compiled DLL where it needs to be so that things will work.
Closed Nov 13, 2015 at 3:34 PM by JanKuceraMSFT
Cannot be fixed in .NET Gadgeteer Core as it is an underlying issue with Socket.

comments

ethicalhacker wrote May 17, 2015 at 3:08 AM

While my fix seems to work fine as long as the device is in debug mode, it stops working once the device is running apart from a PC. Maybe I'm missing something. I can tell from the debug log on my SD card that it's failing again during the StartLocalServer() method presumably with the same issue - the endpoint already in use.

ethicalhacker wrote May 17, 2015 at 3:09 AM

It's worth noting that I'm using a FEZ Raptor.

ethicalhacker wrote May 17, 2015 at 3:28 AM

Further testing reveals that the nothing is what it seems. Much of what I've written concerning a fix has been flawed. The problem is real, but the fix is not. As it turns out, debug and standalone mode actually work the same. My test was flawed. Either will misbehave if one requests something from the WebServer before it goes down due to disconnecting the Ethernet cable.

ethicalhacker wrote Jul 1, 2015 at 1:55 AM

Nobody responded to this issue on the GHI Electronics Forums, so here's what I ended up doing. This is an unacceptable, hacky work around, but it's slightly better than no work around at all.
if (!StringHelper.IsNullOrWhiteSpace(this.webServerIpAddress) && this.webServerIpAddress != ethernet.NetworkInterface.IPAddress)
     {
          Debug.Print("Stopping the old web server because the IP address has changed.");
          //The Server.Stop method called from WebServerManager.StopAll called from WebServer.StopLocalServer doesn't close the socket if a client has ever hit the socket.
          //This means that subsequent StartLocalServer calls would blow up because the socket is in use.
          //It's better to leave the server running and blowing up (because it's polling a dead network connection repeatedly within SocketNative.poll).
           //Only try to stop the server if our IP address has changed.
          WebServer.StopLocalServer();
     }
     if (StringHelper.IsNullOrWhiteSpace(this.webServerIpAddress) || this.webServerIpAddress != ethernet.NetworkInterface.IPAddress)
     {
          WebServer.StartLocalServer(ethernet.NetworkInterface.IPAddress, 80);
          webServerIpAddress = ethernet.NetworkInterface.IPAddress;
     }

wrote Jul 7, 2015 at 2:31 AM

wrote Sep 28, 2015 at 5:01 PM

wrote Nov 13, 2015 at 3:34 PM