数据读取器.即使数据可用,LoadAsync也会无限等待

本文关键字:数据 LoadAsync 等待 无限 读取 | 更新日期: 2023-09-27 18:18:24

我有以下代码在我的Windows phone 8应用程序。

//connection code, done during app start
socket = new StreamSocket();
await socket.ConnectAsync(serverHostName, serviceName);
dataReader = new DataReader(socket.InputStream);
dataReader.InputStreamOptions = InputStreamOptions.Partial;
dataWriter = new DataWriter(socket.OutputStream);

连接建立后,我有另一个线程检查传入的网络数据包

await dataReader.LoadAsync(2048);
dataReader.ReadBytes(buffer);
----------

工作流程如下

  • 电话使用套接字连接到服务器。ConnectAsync
  • 服务器响应初始消息(电话在dataReader中正确接收此消息)。LoadAsync函数)
  • 手机现在发送'业务特定'请求
  • 服务器现在回复'业务特定'响应(问题在于这里。

"工作状态"answers"非工作状态"之间没有场景差异。

所以我试着调试这个。I 为dataReader设置了一个断点。LoadAsync并看到执行在调用时无限等待。

为了确保服务器正确发送数据,我在Windows phone模拟器中运行应用程序,并在PC中运行WireShark网络分析器。我可以看到正在接收电话IP地址的数据包。

有没有人知道为什么dataReader。LoadAsync函数调用根本不返回,当有数据准备在套接字中读取?

数据读取器.即使数据可用,LoadAsync也会无限等待

我也面临同样的问题。这对于蓝牙RFCOMM SPP串行端口设备来说尤其糟糕,因为底层RFCOMM -object不提供设置ReadTimeout值的功能。编辑:InputStreamOptions。部分选项似乎在UWP Win10平台上工作,但只有当您已经知道您期望的大量数据时,它才有用。否则,它将无限期地等待最后一次调用。

我几乎放弃了,当我在这些行下面的参考文献中发现通过使用CancellationTokenSource

来解决问题时
//connect your Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService 
// see the Bluetooth chat example
[...]
StreamSocket streamSocket      = new StreamSocket();  
await streamSocket.ConnectAsync(...); //connect to Bluetooth device
DataReader dataReader = new DataReader(inputStream); // to read from the stream
try
 {
    var timeoutSource   = new CancellationTokenSource(1000); // 1000 ms
    uint numberBytesToRead = 256;
    var data    = await dataReader.LoadAsync(numberBytesToRead).AsTask(timeoutSource.Token);
 }
catch (TaskCanceledException)
{
  // we will get here, and everything looks fine, but the problem is:
  // The underlying streamSocket is also closed!
  // we can not re-use the streamSocket. Any more calls to LoadAsync results in exceptions (something about the object being not assigned...)
  // we need to do a full   await streamSocket.ConnectAsync(...)  again, but that takes ~3 seconds.
}

所以这个方法只是在超时时的最后一招。

@mayu的方法工作得很好(serialDevice.ReadTimeout),但仅适用于Windows.Devices.SerialCommunication类的设备。串行设备,但不在上Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService。我不知道TCP/IP套接字的情况。

简而言之,RFCOMM SPP蓝牙连接是否有可用的超时?或者任何方法提前知道如果。loadasync(1)将阻塞,因为没有新的数据可用?

这个在MSDN上的家伙有完全相同的问题,但是微软也不知道答案:https://social.msdn.microsoft.com/Forums/vstudio/en-US/71ea17d4-ca16-43c2-ab43-02d5301def3f/chow-to-set-timeout-on-streamsocketreadasync?forum=wpdevelop

引用:

在UWP StreamSocket中,我可以超时读取数据并在超时后保持连接打开

https://social.msdn.microsoft.com/Forums/en-US/8a5c4fdc-28d6-4a22-8df6-bc519efeaa4d/how-to-control-the-timeout-for-reading-from-streamsocket?forum=winappswithcsharp

SocketStream的数据读取器

"根据使用InputStreamOptions时的文档。部分,您应该使用UnconsummedBufferLength而不是硬编码值"

那个样品好像坏了。

"await reader.LoadAsync(reader.UnconsumedBufferLength);"相当于

等待reader.LoadAsync (0);然后不可能读取任何数据,因为没有缓冲区可以读取。

我现在正在测试这个,它看起来像"reader"。InputStreamOptions = Partial;"没有任何效果。我唯一的解决方法是降低读取超时

根据文档使用InputStreamOptions。部分,您应该使用UnconsummedBufferLength而不是硬编码的值:

 DataReader reader = new DataReader(clientSocket.InputStream);
 // Set inputstream options so that we don't have to know the data size
 reader.InputStreamOptions = Partial;
 await reader.LoadAsync(reader.UnconsumedBufferLength);  

我有一个类似的问题使用Windows远程Arduino库和SerialUSB流。我不得不改变这个库并调用LoadAsync(1)而不是原来的LoadAsync(100)。现在代码运行正常了。

见:https://github.com/ms-iot/remote-wiring/issues/111

对于串行设备,您需要设置

device.ReadTimeout = TimeSpan.FromMilliseconds(100);

让LoadAsync在buffer满之前返回

我发现在读取之前不知道数据大小的唯一方法是每次读取一个字节,直到我得到超时。感觉很可怕,但很有效。有没有更好的办法?

private async Task ReadData(CancellationToken cancellationToken)
{
    cancellationToken.ThrowIfCancellationRequested();
    DataReaderObject.InputStreamOptions = InputStreamOptions.Partial;
    uint data = 0;
    uint bufferLength = DataReaderObject.UnconsumedBufferLength;
    var timeoutSource = new CancellationTokenSource(100); // 100 ms
    try
    {
        while (true)
        {
            data = await DataReaderObject.LoadAsync(1).AsTask(timeoutSource.Token);
            if (data > 0)
            {
                String temp = DataReaderObject.ReadString(data);
                TemperatureValue.Text += temp.Trim();
            }
        }
    }
    catch (Exception)
    {
        ;
    }
}