StreamSocket:datareader.LoadAsync即使在数据可用时也会等待无限

时间:2014-08-18 13:33:18

标签: c# .net sockets windows-phone-8 windows-runtime

我的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);
----------

工作流程如下

  • 手机使用socket.ConnectAsync
  • 连接到服务器
  • 服务器响应初始消息(Phone在dataReader.LoadAsync函数中正确接收)
  • 手机现在发送“特定业务”请求
  • 服务器现在回复“特定于业务”的响应(问题在于此处。电话在某些时候没有收到服务器的回复。)

工作状态'和'非工作状态'之间没有任何情况差异。

所以我试着调试一下。我为dataReader.LoadAsync设置了一个断点,并看到执行时无限期地等待执行。

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

当有数据准备好在套接字中读取时,任何人都有任何关于dataReader.LoadAsync函数调用根本没有返回的提示吗?

6 个答案:

答案 0 :(得分:2)

我遇到了同样的问题。对于蓝牙RFCOMM SPP串行端口设备尤其不利,因为底层的Rfcomm对象不提供设置ReadTimeout值的功能。 编辑:InputStreamOptions.Partial选项似乎正在使用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.Serial Device的设备,但 on Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService。我不知道TCP / IP套接字的情况如何。

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

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

参考文献:

In UWP StreamSocket, can I read data with timeout and leave the connection open if timeout elapses

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

DataReader of SocketStream for UWP App

答案 1 :(得分:1)

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

该样本似乎已被打破。

"等待reader.LoadAsync(reader.UnconsumedBufferLength);"相当于

等待reader.LoadAsync(0);然后它就无法读取任何数据,因为你没有可以读取的缓冲区。

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

答案 2 :(得分:0)

根据使用InputStreamOptions.Partial时的文档,您应该使用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);  

Sample is there

答案 3 :(得分:0)

我在使用Windows Remote Arduino库和SerialUSB流时遇到了类似的问题。我不得不更改此库并调用LoadAsync(1)而不是原始的LoadAsync(100)。现在代码工作正常。

请参阅:https://github.com/ms-iot/remote-wiring/issues/111

答案 4 :(得分:0)

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

device.ReadTimeout = TimeSpan.FromMilliseconds(100);

在缓冲区已满之前返回LoadAsync。

答案 5 :(得分:0)

The only way I found to get round not knowing the size of the data before reading was to read one byte at a time until I got a timeout. Feels horrid, but works. Is there a better way yet?

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)
    {
        ;
    }
}