【问题标题】:How can I cancel LoadAsync() without closing the underlying socket?如何在不关闭底层套接字的情况下取消 LoadAsync()?
【发布时间】:2019-09-27 19:09:56
【问题描述】:

我有一个应用程序通过蓝牙上的 rfcomm 与某些硬件通信。我的应用程序在 Android 上运行,并且正在让事情在 UWP 上运行。以下是我在 UWP 代码中设置流读取器/写入器的方法:

var btDevice = await BluetoothDevice.FromIdAsync(devId);

var services = await btDevice.GetRfcommServicesAsync();

if (services.Services.Count > 0)
{
    // We only have one service so use the first one...
    var service = services.Services[0];
    // Create a stream...
    _bluetoothStream = new StreamSocket();
    await _bluetoothStream.ConnectAsync(service.ConnectionHostName, service.ConnectionServiceName);

    _dataReader = new DataReader(_bluetoothStream.InputStream);
    _dataWriter = new DataWriter(_bluetoothStream.OutputStream);

    _dataReader.InputStreamOptions = InputStreamOptions.Partial;

我的硬件仅在应用发送数据后才向我的应用发送数据,因此我设置了发送/接收机制。除了我的设备正在重新启动(但蓝牙连接仍然处于活动状态)并且无法发送响应的特定用例之外,一切都很好。在这种情况下,我的上层代码设置为尝试重试,但是当接收超时时蓝牙连接会关闭。

_dataWriter.WriteBytes(comm.TransmitData);

Task<UInt32> writeAysncTask = _dataWriter.StoreAsync().AsTask();

UInt32 bytesWritten = await writeAysncTask;
:
try
{
    using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(comm.TimeoutMs))) // _receiveTimeoutMs)))
    {
        // When this times out, exception gets thrown and socket is closed
        // How do I prevent the socket from closing so I can do a retry???
        var loadTask = _dataReader.LoadAsync(comm.ReceiveCount).AsTask(cts.Token);

        bytesRead = await loadTask;

        if (bytesRead > 0)
        {
            rxData = new byte[bytesRead];
            _dataReader.ReadBytes(rxData);
        }
        else
        {
            System.Diagnostics.Debug.WriteLine("Received 0!");
        }
    }
}
catch (Exception ex)
{
    // The bluetooth connection is closed automatically if the
    // caancellationToken fires...In my case, I need the connection
    // to stay open...How do I achieve this???


    // Update: When this code is executed with _dataReader/Writer
    // that was created with SerialDevice class (see below), the
    // timeout exception does not cause the Serial connection to
    // close so my calling code can then issue a retry.
    System.Diagnostics.Debug.WriteLine(ex.Message) ;
}

更新:应该注意的是,当我对从 SerialDevice 创建的流使用完全相同的代码时,一切都会按我的预期工作......当接收超时时,套接字不会关闭。似乎我在 UWP 的蓝牙实现中遇到了一些问题。啊。以下是我使用 SerialDevice 类创建 _dataReader/_dataWriter 的方法:

_serialDevice = await SerialDevice.FromIdAsync(devId);
// Configure the port
_serialDevice.BaudRate = _baudrate;
_serialDevice.Parity = SerialParity.None;
_serialDevice.DataBits = 8;
_serialDevice.StopBits = SerialStopBitCount.One;

_dataReader = new DataReader(_serialDevice.InputStream);
_dataWriter = new DataWriter(_serialDevice.OutputStream);

标签: c# xamarin uwp bluetooth rfcomm


【解决方案1】:

我想出了一个解决我面临的问题的方法。不幸的是,我不能对 SerialDevice 和 BluetoothDevice 使用相同的代码。我不得不说,当取消令牌超时时,蓝牙套接字被关闭真的很臭。如果它没有关闭,代码会干净得多!难道不应该由我来决定是否应该关闭套接字吗?现在我被这个困住了:

    using (var cts = new CancellationTokenSource())
    {
        Task.Run(async () =>
        {
            try
            {
                await Task.Delay((int)comm.TimeoutMs, cts.Token);

                System.Diagnostics.Debug.WriteLine("Canceling async read");
                // If we make it this far, then the read as failed...cancel the async io
                // which will cause the bytesRead below to be 0.
                await _bluetoothStream.CancelIOAsync();
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }
        }, cts.Token);

        var loadTask = _dataReader.LoadAsync(comm.ReceiveCount).AsTask();

        bytesRead = await loadTask;

        if (bytesRead > 0)
        {
            // SIgnal the delay task to cancel...
            cts.Cancel(true);
            if (bytesRead > comm.ReceiveCount)
                System.Diagnostics.Debug.WriteLine("Received too much!!");

            rxData = new byte[bytesRead];
            _dataReader.ReadBytes(rxData);
        }
        else
        {
            System.Diagnostics.Debug.WriteLine("Received 0!");
        }
    }

在实现这个之后,我确实注意到在我配对了我的 BT 设备后,Windows 在以下查询中将它作为 SerialDevice 返回:

string aqs = SerialDevice.GetDeviceSelector();
DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(aqs);
// My bluetooth device is included in the 'devices' collection

因此,如果我将其作为串行设备连接,我想我毕竟不需要解决这个问题。哦,好吧,希望这篇文章对其他人有所帮助。

【讨论】:

    猜你喜欢
    • 2011-05-20
    • 1970-01-01
    • 1970-01-01
    • 2013-10-04
    • 1970-01-01
    • 2020-01-10
    • 1970-01-01
    • 2013-02-18
    • 1970-01-01
    相关资源
    最近更新 更多