c# - ConnectAsync() 在 C# Visual Studio 中永远不会完成
问题描述
TLDR:为运行 Windows 10 IoT 和 (RFCOMM) 的 Raspberry Pi 3 Model B 创建 UWP 应用程序StreamSockets.ConnectAsync()
有时会完成,有时需要很长时间才能失败(“没有更多数据可用。(HRESULT 异常:0x80070103)”)或只是永远不会完成。这是为什么?Visual Studio 中的调试器已使用但未成功。
我目前正在使用 Visual Studio 2017 创建一个 UWP 应用程序并用 C# 编写。该应用程序正在运行 Windows 10 IoT 的我的 Raspberry Pi 3 Model B 上进行测试,该应用程序的目的是使用 RFCOMM 连接到具有 HC-06 蓝牙模块的 Arduino。
我有一个按钮用于测试 Raspberry Pi 和 Arduino 之间的连接,它将尝试(依次)连接到我定义的所有蓝牙设备。
但是,我遇到了一个“错误”;有时当我单击按钮时,连接测试工作正常,建立连接,我还可以看到发送的数据打印在我的 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由我在其中声明的蓝牙设备数量定义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 开发、网络/蓝牙和 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}'")上的串行端口服务连接时出现问题。问题是这是我的 HC-06 提供的唯一 RFCOMM 服务。
解决方案
推荐阅读
- vue.js - 如何将我的表单中的照片上传到我的 Vuex 商店?
- mysql - 在 laravel 中将多个表与一张表连接起来
- .htaccess - .htaccess 两个旧 URL 到一个新 URL
- php - 如何正确使用多个 SqlSelect 设置/添加 where 函数
- c - 如何在 gdb 中做简单的数学运算
- uwp - UWP:绑定和添加新项目的问题
- python - 卸载 Python 会不会很危险
- java - 使用数组反序列化对象会使用 GSON 返回 null,为什么?
- pandas - 熊猫列表条件和值分配
- android - Logcat 不断推送错误“输入 svInfo.flags 为 8”