sensaura-public / iotweb

A simple HTTP and WebSocket server component for Windows 10 IoT Core and .NET 4.5
38 stars 21 forks source link

System.Net.Sockets.SocketException: The socket is not connected #11

Open sbrl opened 7 years ago

sbrl commented 7 years ago

Hello!

I've been using this library for a project of mine, but I noticed that it's been a bit unstable when a client disconnects prematurely from the server (i.e. refresh a page in your browser that makes several subsequent requests - mine includes a websocket) very quickly, where my application throws an exception with a stack trace that contains only external code frames (see below). I've found some time recently to track down the bug, and I thought you'd like to know about it :smile_cat:

First, let's take a look at that stack trace:

System.Net.Sockets.SocketException: The socket is not connected
  at System.Net.Sockets.Socket.Shutdown (System.Net.Sockets.SocketShutdown how) [0x00030] in <2b0d86369d72459baed0cee98a8e578a>:0
  at IotWeb.Server.SocketServer+<>c__DisplayClass18_1.<Start>b__1 (System.Object e) [0x00072] in <05e83aefcb0042d8898f31f828a2b12a>:0
  at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context (System.Object state) [0x00007] in <a07d6bf484a54da2861691df910339b1>:0
  at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) [0x00071] in <a07d6bf484a54da2861691df910339b1>:0
  at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) [0x00000] in <a07d6bf484a54da2861691df910339b1>:0
  at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem () [0x00021] in <a07d6bf484a54da2861691df910339b1>:0
  at System.Threading.ThreadPoolWorkQueue.Dispatch () [0x00074] in <a07d6bf484a54da2861691df910339b1>:0
  at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback () [0x00000] in <a07d6bf484a54da2861691df910339b1>:0

a little bit about my environment:

....more environment information available on request.

Let's take a look at that stack trace. On first inspection, the 2nd at line looks to be at fault: at IotWeb.Server.SocketServer+<>c__DisplayClass18_1.<Start>b__1

However, a quick search of this repository via GitHub confirms that there's no method in IotWeb with that signature. Drat. We're not foiled yet though - let's investigate the previous line: at System.Net.Sockets.Socket.Shutdown

A quick search of the repository for Socket.Shutdown via GitHub reveals that Socket.Shutdown is only called in one place, which leads me to line #115 of IotWeb NET45/SocketServer.cs.

I've no idea how IotWeb is laid out (where's the http parser? I can't find it! :stuck_out_tongue:, but from what I can see, the issue lies in the fact that the client.Shutdown() call on line 115 is both inside a queued work item from the ThreadPool and not inside the try block. understandably, we want this code to run regardless of whether an exception is thrown inside the try block (perhaps a finally clause would be appropriate here?), but we also want to catch any exceptions thrown here. I can see 2 solutions:

  1. Wrap the client.Shutdown() call in second try..catch block
  2. Figure out a way to catch and eat the exception from the ThreadPool (stackoverflow has some thoughts on this). From what I can see, option 1 is the easiest solution here, but it's also suggested that one uses Task.Run instead. Perhaps https://blogs.msdn.microsoft.com/ptorr/2014/12/10/async-exceptions-in-c/ is worth a read.

From my investigations, I would advocate solution 1 here - I'll send a PR if you like.