Search code examples
c#uwpraspberry-piiotwindows-10-iot-core

StreamSocketListener port randomly hangs on Windows IoT UWP application


I have a UWP background app that I wrote which runs on a Raspberry Pi running the latest build of Windows IoT. It's purpose is to handle http requests and return a response to several mobile apps.

It works great, except that it seems like the listening port on the socket will occasionally hang. The rest of the background app remains responsive so I know the app is still running. But the http requests stop responding. Usually just logging into the IoT Dashboard and restarting my app will resolve the problem. But I can't figure out why the app occasionally hangs.

Here is the segment of code that I think is relevant:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using Windows.ApplicationModel.Background;
using Windows.Networking.Sockets;

namespace MyApp.Sandbox
{
    public class SystemTest : IBackgroundTask
    {
        private StreamSocketListener listener = null;

        public void Run(IBackgroundTaskInstance taskInstance)
        {
            StartServer();
        }

        public async void StartServer()
        {
            try
            {
                int port = 8189;

                listener = new StreamSocketListener();
                var currentSetting = listener.Control.QualityOfService;
                listener.Control.QualityOfService = SocketQualityOfService.LowLatency;

                await listener.BindServiceNameAsync(port.ToString());

                listener.ConnectionReceived += async (sender, args) =>
                {
                    var request = new StringBuilder();
                    using (var input = args.Socket.InputStream.AsStreamForRead())
                    {
                        int maxBuffer = 8192;
                        int index = 0;

                        List<byte> line = new List<byte>();
                        int b;
                        while (true)
                        {
                            try
                            {
                                b = input.ReadByte();
                                line.Add((byte)b);

                                if (b == 10 && line.Count >= 2)
                                {
                                    if (line[line.Count - 2] == 13 && line[line.Count - 1] == 10)
                                    {
                                        byte[] byteArray = line.ToArray();
                                        var dataString = Encoding.UTF8.GetString(byteArray, 0, byteArray.Length);
                                        request.Append(dataString);
                                        line = new List<byte>();

                                        if (request.ToString().StartsWith("GET"))
                                        {
                                            break;
                                        }

                                        if (request.ToString().StartsWith("POST") &&
                                            request.ToString().Contains(Environment.NewLine + Environment.NewLine))
                                        {
                                            string contentLength = GetValueFromHeader("Content-Length", request.ToString());
                                            if (!contentLength.Equals(""))
                                            {
                                                int length = Convert.ToInt32(contentLength);
                                                if (length > 0)
                                                {
                                                    byteArray = new byte[length];
                                                    input.Read(byteArray, 0, length);
                                                    dataString = Encoding.UTF8.GetString(byteArray, 0, byteArray.Length);
                                                    request.Append(dataString);
                                                }
                                            }
                                            break;
                                        }
                                    }
                                }

                                index++;

                                if (index > maxBuffer)
                                {
                                    break;
                                }
                            }
                            catch
                            {
                                break;
                            }
                        }
                        input.Dispose();
                    }

                    using (var output = args.Socket.OutputStream)
                    {
                        using (var response = output.AsStreamForWrite())
                        {
                            var requestLines = request.ToString().Split(' ');
                            var url = requestLines.Length > 1 ? requestLines[1] : string.Empty;

                            string postLine = null;
                            if (requestLines.Length > 0)
                            {
                                if (requestLines[0] == "POST")
                                {
                                    postLine = requestLines[requestLines.Length - 1];
                                }
                            }

                            string str = null;
                            try
                            {
                                // handle response here which fills "str"
                            }
                            catch (Exception innerEx)
                            {
                                Debug.WriteLine(innerEx.Message);
                                Debug.WriteLine(innerEx.StackTrace);
                            }

                            var html = Encoding.UTF8.GetBytes(str);
                            using (var bodyStream = new MemoryStream(html))
                            {
                                var header = $"HTTP/1.1 200 OK\r\nContent-Length: {bodyStream.Length}\r\nContent-Type: application/json\r\ncharset: UTF-8\r\nConnection: close\r\n\r\n";
                                var headerArray = Encoding.UTF8.GetBytes(header);
                                await response.WriteAsync(headerArray, 0, headerArray.Length);
                                await bodyStream.CopyToAsync(response);
                                await response.FlushAsync();
                            }
                        }
                        output.Dispose();
                    }
                };
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
                Debug.WriteLine(ex.StackTrace);
            }
        }
    }
}

I have confirmed that "Compile with .NET Native Toolchain" is enabled in my build configuration. And I am running this in release mode.

I am not sure if there is something different I need to be doing to release the incoming connections? Is it perhaps hanging because multiple requests are coming in at the same time and it can't handle it?

Here is an example of what netstat looks like when the port (in this case it is 8081) hangs:

  TCP    192.168.1.190:8081     5.101.0.209:32880      CLOSE_WAIT
  TCP    192.168.1.190:8081     5.101.0.209:33494      CLOSE_WAIT
  TCP    192.168.1.190:8081     5.101.0.209:60412      CLOSE_WAIT
  TCP    192.168.1.190:8081     Mike-PC:59879          CLOSE_WAIT
  TCP    192.168.1.190:8081     Mike-PC:59880          CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:43096    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:43110    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:43114    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:43120    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:43130    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:43742    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:43744    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:43750    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:43822    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:43914    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:43986    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:48646    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:48648    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:48702    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:48724    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:48774    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:48776    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:48820    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:48834    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:48918    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:49036    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:49058    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:49284    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:49416    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:49672    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:49680    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:50360    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:50566    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:50718    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:50724    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:50734    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:50938    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:51082    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:51524    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:51880    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:51946    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:52146    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:52212    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:52224    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:52310    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:53126    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:53206    CLOSE_WAIT
  TCP    192.168.1.190:8081     Galaxy-S20-5G:53284    CLOSE_WAIT
  TCP    192.168.1.190:8081     192.227.118.82:1550    ESTABLISHED
  TCP    192.168.1.190:59613    52.242.211.89:https    ESTABLISHED
  TCP    [::]:22                myapp:0                LISTENING
  TCP    [::]:135               myapp:0                LISTENING
  TCP    [::]:445               myapp:0                LISTENING
  TCP    [::]:5985              myapp:0                LISTENING
  TCP    [::]:8080              myapp:0                LISTENING
  TCP    [::]:8081              myapp:0                LISTENING
  TCP    [::]:47001             myapp:0                LISTENING
  TCP    [::]:49664             myapp:0                LISTENING
  TCP    [::]:49665             myapp:0                LISTENING
  TCP    [::]:49667             myapp:0                LISTENING
  UDP    0.0.0.0:123            *:*                    
  UDP    0.0.0.0:5050           *:*                    
  UDP    0.0.0.0:5353           *:*                    
  UDP    0.0.0.0:5355           *:*                    
  UDP    0.0.0.0:29819          *:*                    
  UDP    0.0.0.0:51049          *:*                    
  UDP    [::]:123               *:*                    
  UDP    [::]:5353              *:*                    
  UDP    [::]:5355              *:*                    

At this point I am open to suggestions if anybody else has run into a similar situation....

Thanks!


Solution

  • You may try to add StreamSocket.Dispose Method to abort any pending operations and releases all unmanaged resources associated with the StreamSocket object.

                listener.Control.KeepAlive = true;
    
                listener.ConnectionReceived += async (sender, args) =>
                {
                    //Your codes
    
    
                    args.Socket.Dispose();
                };