ConnectAsync () никогда не заканчивается в C # Visual Studio - PullRequest
0 голосов
/ 06 сентября 2018

TLDR: Создание приложения UWP для Raspberry Pi 3 Model B под управлением Windows 10 IoT и StreamSockets.ConnectAsync() (RFCOMM) иногда завершается, а иногда для сбоя требуется чрезвычайно много времени («Больше данных нет доступны (исключение из HRESULT: 0x80070103) ") или просто никогда не заканчивается. Это почему? Отладчик в Visual Studio использовался безуспешно.

В настоящее время я создаю приложение UWP с использованием Visual Studio 2017 и пишу на C #. Приложение тестируется на моем Raspberry Pi 3 Model B под управлением Windows 10 IoT, и целью приложения является подключение к Arduino с Bluetooth-модулем HC-06 с использованием RFCOMM.

У меня есть кнопка для тестирования соединения между Raspberry Pi и Arduino, которая попытается (по очереди) подключиться ко всем устройствам Bluetooth, которые я определил.

Я, однако, столкнулся с «ошибкой»; иногда, когда я нажимаю кнопку, тест соединения работает нормально, соединение устанавливается, и я также вижу отправленные данные, напечатанные в Serial Monitor на моем Arduino. Проблема заключается в том, что иногда вызов StreamSockets.ConnectAsync() просто никогда не завершается или использует необычайно длительное время для сбоя с исключением «Нет больше данных. (Исключение из HRESULT: 0x80070103)».

Мой вопрос: что может быть причиной такого поведения?

Весь код ниже находится в Settings.xaml.cs.

public sealed partial class Settings : Page
{
    private MainPage rootPage = Current;

    private StreamSocket socket = null;
    private DataWriter writer = null;
    private RfcommDeviceService service = null;
    private BluetoothDevice bluetoothDevice;

    public Settings()
    {
        InitializeComponent();
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        rootPage = Current;
    }

    protected override void OnNavigatedFrom(NavigationEventArgs e)
    {
        Disconnect();
    }

    void App_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
    {
        Disconnect("App suspension discconects");
    }

// Rest of the code is below here //

}

Ход моей программы:

При нажатии кнопки вызывается ConnectionTestButton_Click:

private void ConnectionTestButton_Click(object sender, RoutedEventArgs e)
{
    ConnectionTest();
}

Который в свою очередь вызывает ConnectionTest():

private async void ConnectionTest()
{
    rootPage.NotifyUser("Connection test in progress...", NotifyType.StatusMessage);
    Disconnect(); // Ensures that there is no residue data from previous attempts

  // This connects to the Bluetooth devices I have specified in MainPage.xaml.cs in turn
  // There is no problem with the declaration of the devices as this works correctly
    foreach (KeyValuePair<string, string> device in Constants.BtDevices)
    {
        await Task.Run(() => ConnectToBtDevice(device.Value, true));
        await Task.Delay(5000);
        Disconnect("Connection test");
        await Task.Delay(5000);
    }
}

Какие вызовы ConnectToBtDevice() x раз, где x определяется тем, сколько устройств Bluetooth я объявил в MainPage.xaml.cs:

private async Task ConnectToBtDevice(string btId, bool connectionTest = false)
{
    try
    {
        bluetoothDevice = await BluetoothDevice.FromIdAsync(btId);
        rootPage.NotifyUser(
                 Constants.BtDevices.FirstOrDefault(x => x.Value == btId).Key
                 + " found, trying to connect...",
                 NotifyType.StatusMessage);
    }
    catch (Exception ex)
    {
        rootPage.NotifyUser(
                 "An exception was thrown when trying to receive the Bluetooth device "
                 + ex.Message,
                 NotifyType.ErrorMessage);
        return;
    }

    if (bluetoothDevice == null)
    {
        rootPage.NotifyUser("Bluetooth device returned null",
                 NotifyType.ErrorMessage);
    }

    var rfcommServices = await bluetoothDevice.GetRfcommServicesAsync();
    if (rfcommServices.Services.Count > 0)
    {
        service = rfcommServices.Services[0];
    }
    else
    {
        rootPage.NotifyUser("No RFCOMM services found on the device",
                 NotifyType.ErrorMessage);
        return;
    }

    lock (this)
    {
        socket = new StreamSocket();
    }
    try
    {
      // This call is what is causing my headache
        await socket.ConnectAsync(service.ConnectionHostName,
                     service.ConnectionServiceName,
                     SocketProtectionLevel
                      .BluetoothEncryptionAllowNullAuthentication);

        rootPage.NotifyUser("Hostname: "
                 + service.ConnectionHostName
                 + " Servicename: "
                 + service.ConnectionServiceName,
                 NotifyType.StatusMessage);

    // Ensure utf8 is used for the reader and writer
    // Not sure if this is actually necessary?
        writer = new DataWriter(socket.OutputStream)
        {
            UnicodeEncoding = UnicodeEncoding.Utf8
        };
        DataReader reader = new DataReader(socket.InputStream)
        {
            UnicodeEncoding = UnicodeEncoding.Utf8
        };

        rootPage.NotifyUser("Connected to "
                 + Constants.BtDevices.FirstOrDefault(x => x.Value == btId).Key,
                 NotifyType.SuccessMessage);

        ReceiveData(reader, connectionTest);

     // Send some data if the connection is for test purposes only
        if (connectionTest)
        {
            await SendData("Test");
        }
    }
    catch (Exception ex)
    {
        rootPage.NotifyUser("An exception was thrown when trying to connect to "
                 + Constants.BtDevices.FirstOrDefault(x => x.Value == btId).Key
                 + " "
                 + ex.Message,
                 NotifyType.ErrorMessage);
        Disconnect();
    }
}

Также вызывается Disconnect, если соединение установлено и данные отправлены:

private void Disconnect(string disconnectReason = null)
{
    if (writer != null)
    {
        writer.DetachStream();
        writer = null;
    }

    if (service != null)
    {
        service.Dispose();
        service = null;
    }
    lock (this)
    {
        if (socket != null)
        {
            socket.Dispose();
            socket = null;
        }
    }
    if (disconnectReason != null)
    {
        rootPage.NotifyUser("Disconnected. Reason: "
                 + disconnectReason,
                 NotifyType.CautionMessage);
    }
}

(Если какой-либо код странно с отступом / отформатирован, это, вероятно, потому, что я пытался ограничить длину строки для этого вопроса, поэтому я быстро отформатировал его в текстовом редакторе вопроса)

Я пробовал отладчик в Visual Studio, но безуспешно обнаружил, что не так, поскольку все кажется нормальным. Под нормальным я подразумеваю, что каждая переменная - это то, чем она должна быть, например, service.ConnectionHostName и service.ConnectionServiceName установлены на правильные значения. {(XX:XX:XX:XX:XX:XX)} и "Bluetooth#BluetoothbX:XX:XX:XX:XX:XX-XX:XX:XX:XX:XX:XX#RFCOMM:XXXXXXXX:{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}" соответственно ("X'ed" для конфиденциальности (не то, чтобы это действительно так важно, но лучше, чем потом сожалеть;)))

Ни SendData(), ни ReceiveData() на самом деле не имеют отношения к этому вопросу, так как проблема заключается до вызова любого из этих методов, но я с радостью включу их, если потребуется.

Могу также добавить, что я использовал образец BluetoothRfcommChat в качестве справочного материала, поскольку я очень новичок в разработке UWP, работе с сетями / Bluetooth и C # в целом.

(Также дайте мне знать, если что-то не так с форматированием самого вопроса)

Обновление:

Иногда, когда для установления соединения требуется слишком много времени, я могу встретить исключение: «Попытка подключения не удалась, потому что подключенная сторона не ответила должным образом через некоторое время, или не удалось установить соединение, потому что подключенный хост не смог ответить. «

Обновление 2:

Я добавил CancellationToken, чтобы иметь функцию тайм-аута для метода ConnectAsync, однако ConnectAsync только иногда регистрирует запросы отмены, что означает, что иногда он все еще застрял.

Как я это реализовал:

// Called in constructor
private CancellationTokenSource connectionCts = null;

// Called in 'ConnectoToBtDevice' (same place as original)
try
{
  // public const int ConnectionTimeout = 5000;
    connectionCts = new CancellationTokenSource();
    connectionCts.CancelAfter(Constants.ConnectionTimeout);
    await socket.ConnectAsync(service.ConnectionHostName,
                 service.ConnectionServiceName)
                  .AsTask(connectionCts.Token);

  ...

}
catch (TaskCanceledException tEx)
{
    rootPage.NotifyUser("Connection timed out.", NotifyType.ErrorMessage);
    Disconnect();
}

// Called in 'Disconnect'
if (connectionCts != null)
{
    connectionCts.Dispose();
    connectionCts = null;
}

Обновление 3:

Похоже, что если я попытаюсь использовать:

await bluetoothDevice.GetRfcommAsync(BluetoothCacheMode.Uncached);

(Использование BluetoothCacheMode.Uncached) Вместо этого он застревает в этой строке. Я понятия не имею, связано ли это с вышеуказанной проблемой, но может.

Обновление 4:

Хорошо, теперь я немного протестировал и обнаружил, что у него нет проблем с подключением к моему телефону. Это создает впечатление, что существует проблема с подключением с использованием службы последовательного порта, установленной на моих модулях HC-06 (UUID = "{00001101-0000-1000-8000-00805F9B34FB} '"). Проблема в том, что это единственная служба RFCOMM, которую предоставляет мой HC-06.

...