【问题标题】:StreamSocketListener randomly receive empty from client on Raspberry Pi 3 (IoT Core UWP)StreamSocketListener 在 Raspberry Pi 3(IoT Core UWP)上从客户端随机接收空
【发布时间】:2018-03-27 14:22:59
【问题描述】:

我正在测试一个非常简单的 Web 服务器代码(从 MSDN 示例或我忘记的某个地方复制它),我在带有 IoT Core v.10.0.16299.309 的 Raspberry Pi 3 Model B 上运行它。我正确地完成了所有工作,包括在每个请求上处理套接字,并且代码大部分时间都有效。

我只有一个客户端,每 30 秒向该 Web 服务发送 3 个非常简单的 HTTP GET 请求,只是一个像这样的简单 URL:http://serverIP/get?id=123 它正确返回结果,但在一小时内随机返回,请求对象为空出于某种奇怪的原因。我不知道为什么。

private StreamSocketListener _listener;
private int _bufferSize = 8192;

public async void Run(IBackgroundTaskInstance taskInstance)
{
    taskInstance.GetDeferral();
    _listener = new StreamSocketListener();
    _listener.ConnectionReceived += HandleRequest;
    await _listener.BindServiceNameAsync("9080");       
}

public async void HandleRequest(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
  try 
  {
    StringBuilder request = new StringBuilder()
    using (IInputStream input = args.Socket.InputStream)
    {
         byte[] data = new byte[_bufferSize];
         IBuffer buffer = data.AsBuffer();
         uint bytesRead = _bufferSize;
         while (bytesRead == _bufferSize)
         {
            await input.ReadAsync(buffer, _bufferSize, InputStreamOptions.Partial);                        
            request.Append(Encoding.UTF8.GetString(data, 0, data.Length));
                    bytesRead = buffer.Length;

             // Some other code here to process the request object
             // and respond it back to the client. They are irrelevant.

          } // while
      } // using
   } // catch
   catch (Exception ex)
   {
      // Log here
   }
   finally 
   { 
       args.Socket.Dispose();
   }
} // method

我认为线程有问题,所以我禁用了大部分异步和等待。有趣的是,结果要好得多。请求对象是 99% 的罚款。 12小时内,只有3-4次请求为空。我认为这不是正确的解决方案......但我真的被困在这个问题上并且无法弄清楚为什么。任何帮助表示赞赏。

private StreamSocketListener _listener;
private int _bufferSize = 8192;

public async void Run(IBackgroundTaskInstance taskInstance)
{
    taskInstance.GetDeferral();
    _listener = new StreamSocketListener();
    _listener.ConnectionReceived += HandleRequest;
    _listener.BindServiceNameAsync("9080");     // ** No "await"
}

 // ** No "async"
public void HandleRequest(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
  try 
  {
    StringBuilder request = new StringBuilder()
    using (IInputStream input = args.Socket.InputStream)
    {
         byte[] data = new byte[_bufferSize];
         IBuffer buffer = data.AsBuffer();
         uint bytesRead = _bufferSize;
         while (bytesRead == _bufferSize)
         {
            // ** No "await"
            input.ReadAsync(buffer, _bufferSize, InputStreamOptions.Partial);                        
            request.Append(Encoding.UTF8.GetString(data, 0, data.Length));
                    bytesRead = buffer.Length;

             // Some other code here to process the request object
             // and respond it back to the client. They are irrelevant.

          } // while
      } // using
   } // catch
   catch (Exception ex)
   {
      // Log here
   }
   finally 
   { 
       args.Socket.Dispose();
   }
} // method

【问题讨论】:

  • taskInstance.GetDeferral() - 这将获得一个最终将被 GC 处理的临时对象。当对象被 GC 时,它(可能)会自动完成延迟(有些对象会,有些不会)。您可以尝试保持延迟(例如,在类字段中),然后在完成后明确地Complete 它吗?
  • 嗨,彼得.. 感谢您的回复。想我也试过了,_deferral.Completed() 执行后,StreamSocketListen 将不再监听。如果我注释掉 _deferral.Complete() 但保留类变量,则行为与我描述的完全相同。如果我误解了你的意思,请纠正我。
  • @userb00,你是否在响应的标题中添加了Connection: close
  • 是的,我确实将响应发送为 HTTP/1.1 200 OK,然后是 Content-Length:xxx,然后是 Connection:close

标签: c# uwp raspberry-pi3 windowsiot windows-iot-core-10


【解决方案1】:

IInputStream.ReadAsync() 文档有一些有趣的信息可能会有所帮助:

总是从返回的缓冲区中读取数据 IAsyncOperationWithProgress(IBuffer,UInt32)。不要假设 输入缓冲区包含数据。根据实施情况, 读取的数据可能会被放入输入缓冲区,或者可能是 在不同的缓冲区中返回。对于输入缓冲区,您没有 实现 IBuffer 接口。相反,您可以创建一个 Buffer 类的实例。

提供的缓冲区可能不包含数据。

您可以使用返回缓冲区并从中提取数据,也可以使用DataReader.ReadBuffer()(在您的情况下为DataReader.ReadString())从流中获取数据。

一般来说,使用DataReader 从流中读取数据比使用低级 API 更容易。

你的代码变成了

 byte[] data = new byte[_bufferSize];
 IBuffer buffer = data.AsBuffer();
 uint bytesRead = _bufferSize;
 while (bytesRead == _bufferSize)
 {
    var readFromStreamBuffer = await input.ReadAsync(buffer, _bufferSize, InputStreamOptions.Partial);
    var readBytes = readFromStreamBuffer.ToArray();            
    request.Append(Encoding.UTF8.GetString(readBytes, 0, readBytes.Length));
    bytesRead = readFromStreamBuffer.Length;
  }

或者DataReader.ReadBuffer():

 byte[] data = new byte[_bufferSize];
 IBuffer buffer = data.AsBuffer();
 uint bytesRead = _bufferSize;
 var dataReader = new DataReader(input);
 while (bytesRead == _bufferSize)
 {
    await dataReader.LoadAsync(_bufferSize);

    var readFromStreamBuffer = dataReader.ReadBuffer();
    var readBytes = readFromStreamBuffer.ToArray();            
    request.Append(Encoding.UTF8.GetString(readBytes, 0, readBytes.Length));

    bytesRead = readFromStreamBuffer.Length;
  }

或者DataReader.ReadString():

 byte[] data = new byte[_bufferSize];
 IBuffer buffer = data.AsBuffer();
 uint bytesRead = _bufferSize;
 var dataReader = new DataReader(input);
 dataReader.UnicodeEncoding  = UnicodeEncoding.Utf8;
 while (bytesRead == _bufferSize)
 {
    await dataReader.LoadAsync(_bufferSize);
    request.Append(dataReader.ReadString(dataReader.UnconsumedBufferLength));

    bytesRead = readFromStreamBuffer.Length;
  }

【讨论】:

  • 感谢文森特,这是关于 ReadSync() 的非常有趣的信息...实际上有助于阐明这个问题!我正在拉头发,昨晚,我没有主意并更改了我的代码以使用 Socket.InputStream.AsStreamForRead() 只是为了读取一行进行测试(因为我需要的实际上已经足够好了)而且它似乎很稳定,没有随机的空传入请求。我将发布我的代码,它非常简单。但我一定会尝试你的建议。
  • 您的提议有效,因为由 AsStream() 创建的“旧”.Net 流隐藏了 IInputstream.ReadAsync() 的复杂性。您应该直接使用 UWP DataReader 而不是创建一个最终将以 Stream/Data Reader 实例结束的临时 Stream :)
【解决方案2】:

没有想法,我将代码更改为使用 Socket.InputStream.AsStreamForRead() 代替,然后使用 .ReadLineAsync() 仅读取第一行以进行测试。看起来很稳定,过了12个多小时没有空请求!!!

对于我所需要的,这实际上很好,因为我并没有真正构建一个功能齐全的 Web 服务器,所以我不需要获取完整的请求。我只需要获取 HTTP GET QueryString 并将参数传递给我的 Raspberry Pi 的 GPIO。

正如文森特建议的那样,.ReadAsync() 有一些非常有趣的行为。我以后一定会尝试他的建议。

public async void HandleRequest(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
    string requestString;
    try
    { 
        Stream inStream = args.Socket.InputStream.AsStreamForRead();
        StreamReader reader = new StreamReader(inStream);
        requestString = await reader.ReadLineAsync();

         // Some other code here to process the request object
         // and respond it back to the client. They are irrelevant
    } // catch     
    catch (Exception ex)
    {
      // Log here
    }
    finally 
    { 
       args.Socket.Dispose();
    }
}

【讨论】:

    猜你喜欢
    • 2020-07-26
    • 2021-02-17
    • 1970-01-01
    • 2018-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多