StreamSocket: datareader.LoadAsync waits for infanta even if data is avaialbe

I have the following code in a Windows Phone 8 app.

//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);

      

Once the connection is established, I have another thread that checks the incoming network packets

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

      

The workflow looks like this

  • Phone connects to server using socket.ConnectAsync
  • The server replies with the original message (the phone correctly accepts this in the dataReader.LoadAsync function)
  • The phone is currently sending a "business specific" request
  • The server now responds with a "business specific" response ( The problem lies here. The phone does not receive a response from the server at some points in time).

There is no distinction between working state and non-working state.

So, I tried to debug this. I put a breakpoint on dataReader.LoadAsync and saw that execution waits indefinitely for a call.

To make sure the server was sending data correctly, I ran the app in a Windows phone emulator and ran the WireShark sniffer on my PC. I could see that packets are being received for the IP address of the phone.

Are there any hints as to why the dataReader.LoadAsync function call doesn't return at all when there is data in the juice ready to be read?

+3


source to share


6 answers


I faced the same problem. This is especially bad for Bluetooth serial port RFCOMM SPP devices, because the underlying Rfcomm object does not provide the ability to set ReadTimeout values. Edit: The InputStreamOptions.Partial seems to work on the Win10 UWP platform, but this is only useful when you already know a lot of the data you are expecting. Otherwise, it will wait indefinitely on the last call.

I almost gave up when I found in the links below these lines to solve the problem using 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.
}

      

So this method is only a brute-force attempt, the last attempt in a timeout.

The method from @mayu works very well (serialDevice.ReadTimeout), but only on devices of class Windows.Devices.SerialCommunication.Serial Device, but not on Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService. I don't know how the situation is for TCP / IP sockets.

In short, is there a timeout for RFCOMM SPP Bluetooth connections? Or is there any way to know ahead of time if .LoadAsync (1) will be blocked because no new data is available?



This colleague on MSDN has the same problem, but MS doesn't know the answer: https://social.msdn.microsoft.com/Forums/vstudio/en-US/71ea17d4-ca16-43c2-ab43-02d5301def3f/chow-to- set-timeout-on-streamsocketreadasync? forum = wpdevelop

Literature:

In UWP StreamSocket I can read data with a timeout and keep the connection open if timed out

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 for SocketStream for UWP app

+2


source


"According to the documentation, when using InputStreamOptions.Partial, you must use UnconsummedBufferLength instead of a hardcoded value

This pattern appears to be broken.

"waiting for read .LoadAsync (reader.UnconsumedBufferLength);" equivalent to



waiting to read .LoadAsync (0); and then it is not possible to read any data, since you have no buffer to read.

I am testing this now and it looks like "reader.InputStreamOptions = Partial"; has no effect. My only workaround is to lower the read timeout.

+1


source


According to the documentation, when using InputStreamOptions.Partial, you should use UnconsummedBufferLength instead of a hard-coded value:

 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

0


source


I had a similar problem using Windows Remote Arduino library and SerialUSB stream. I had to change this library and call LoadAsync (1) instead of the original LoadAsync (100). The code now works fine.

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

0


source


For serial devices, you need to install

device.ReadTimeout = TimeSpan.FromMilliseconds(100);

so that LoadAsync returns before the buffer is full.

0


source


The only way I have come across not knowing the size of the data before reading is to read one byte at a time until I get a timeout. Feels awful, but works. Is there an even better way?

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

      

0


source







All Articles