One of the more significant challenges that we have faced during our last project presented itself to us about halfway through the project timeline. Our .Net Micro Framework application regularly sends messages to remote endpoints out on the internet over HTTPS/TCP. In isolation a single HTTPS request using the .Net Micro Framework was a pain free operation (even if there is no HTTPS stack out of the box). However, the problem was that after an indeterminate period of time our application would stop sending HTTP requests and would die a horrible death.
To confound matters further no exception were being raised and so we were not even able to catch the exception to a) gather more information or b) gracefully handle the error. Instead the board would just lock up and stop working, as the .Net Micro Framework runtime had crashed.
Given that we were not receiving an exception of any kind initial testing focused on the hardware that we were running on. We tested our software on both the GHI Master Development System and the Tahoe II board from Device Solutions Ltd yet both products exhibited the same problems.
Focus shifted back to software, but given that no exceptions were throw and connecting to the device using the MF Deploy tool yielded no additional insight we could not ascertain the cause of the problem. Having scoured the code-base for any clues we decided to approach Microsoft with our rather odd problem.
After discussing the many intricacies of our application with the .Net Micro Framework team they provided us with an early build of the MF Deploy tool that was to be released with version 4 of the Framework. As soon as we started using this new version of MF deploy we started making progress.
The first thing that happened is we started to get an exception and stack trace information in the MF Deploy trace information. Now we had something to work from!
#### Exception System.Net.Sockets.SocketException - E_FAIL (1) ####
#### Microsoft.SPOT.Net.SocketNative::socket [IP: 0000] ####
#### System.Net.Sockets.Socket::.ctor [IP: 001f] ####
#### SocketException ErrorCode = 10024
A brief search using Reflector quickly led us to a brief description of the problem:
OK, so apparently we have opened too many sockets. Unfortunately; as is so often the case; this only succeeded in posing more questions than it answered.
Our HTTPS/TCP code opened and closed a socket on every request. Our Socket object was also wrapped in a using statement which meant that it would be disposed of after every call.
IPAddress address = GetHostAddress(uri);
IPEndPoint endpoint = new IPEndPoint(address, 80);
using (Socket socket = new Socket(
AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp))
{
//open the socket
socket.Connect(endpoint);
//HTTP writing code removed
//to keep sample terse.
//close the socket
socket.Close();
//dispose the socket
}
So then why do we have too many sockets open? Another look at Reflector only served to strengthen our resolve that we had done everything correctly. The Dispose method did appear to clean up any of the native resources used by the Socket object:
Confused once more we approached Microsoft for an answer to our problems.
It was then that they informed us that by default when you close a Socket in the .Net Micro Framework it does not actually release the Socket for a further 2 minutes! This value is an option that you can set yourself with the following line of code:
socket.SetSocketOption(SocketOptionLevel.Tcp
, SocketOptionName.Linger
, new byte[] { 0, 0, 0, 0 });
So what is SocketOptionName.Linger? Assuming that it is the same as its big brother equivalent in the .Net Framework it is an option that is used to tell the socket to wait before closing so that any unsent data can be sent.
It should only be necessary to set the Linger time to 0 if you are expecting to open another Socket to the same address and port. If you are to set the Linger time to 0 then it is also recommended that you also set the NoDelay option to true so that any messages are sent immediately.
socket.SetSocketOption(SocketOptionLevel.Tcp
, SocketOptionName.NoDelay
, true);
Complete example:
IPAddress address = GetHostAddress(uri);
IPEndPoint endpoint = new IPEndPoint(address, 80);
using (Socket socket = new Socket(
AddressFamily.InterNetwork
,SocketType.Stream
,ProtocolType.Tcp))
{
socket.SetSocketOption(SocketOptionLevel.Tcp
, SocketOptionName.Linger
, new byte[] { 0, 0, 0, 0 });
socket.SetSocketOption(SocketOptionLevel.Tcp
, SocketOptionName.NoDelay, true);
//open the socket
socket.Connect(endpoint);
//HTTP writing code removed
//to keep sample terse.
//close the socket
socket.Close();
//dispose the socket
}
While we expecienced this problem using version 3 of the .Net Micro Framework, I am not aware whether the default behaviour has been changed in version 4.
This was the first (and simplest) of several intricacies in the .Net Micro Framework that we encountered during the course of this project. More in another blog post soon.
Author: Gavin Osborn
@gavinosborn
Tweet