Symbian C++ - 使用 RHostResolver 进行超时同步蓝牙发现

发布于 2024-09-11 11:21:27 字数 2422 浏览 5 评论 0原文

我正在用 Qt 编写一个应用程序,部署在 Symbian S60 平台上。不幸的是,它需要具有蓝牙功能 - 没有什么真正先进的,只是简单的 RFCOMM 客户端套接字和设备发现。确切地说,该应用程序预计可在两个平台上运行 - Windows PC 和前面提到的 S60。

当然,由于 Qt 缺乏蓝牙支持,因此必须使用本机 API 进行编码 - Windows 上的 Winsock2 和 S60 上的 Symbian C++ - 我正在编写一个简单的抽象层。我对 Symbian 上的发现部分有一些问题。

抽象层中的发现调用应该同步工作 - 它会阻塞直到发现结束并将所有设备作为 QList 返回。我现在没有确切的代码,但我有类似的东西:

RHostResolver resolver;
TInquirySockAddr addr;
// OMITTED: resolver and addr initialization

TRequestStatus err;
TNameEntry entry;
resolver.GetByAddress(addr, entry, err);
while (true) {
    User::WaitForRequest(err);
    if (err == KErrHostResNoMoreResults) {
       break;
    } else if (err != KErrNone) {
        // OMITTED: error handling routine, not very important right now
    }

    // OMITTED: entry processing, adding to result QList

    resolver.Next(entry, err);
}
resolver.Close();

是的,我知道 User::WaitForRequest邪恶,这种编码类似于 Symbian,我应该使用活动对象,等等。但这不是我需要的。我需要一种简单、同步的方式来进行设备发现。

上面的代码确实可以工作。然而,有一个怪癖——我想在发现过程中有一个暂停。也就是说,我希望发现时间不超过 15 秒——在函数调用中进行参数化。我尝试做这样的事情:

RTimer timer;
TRequestStatus timerStatus;
timer.CreateLocal();

RHostResolver resolver;
TInquirySockAddr addr;
// OMITTED: resolver and addr initialization

TRequestStatus err;
TNameEntry entry;

timer.After(timerStatus, timeout*1000000);

resolver.GetByAddress(addr, entry, err);
while (true) {
    User::WaitForRequest(err, timerStatus);

    if (timerStatus != KRequestPending) { // timeout
        resolver.Cancel();
        User::WaitForRequest(err);
        break;
    }

    if (err == KErrHostResNoMoreResults) {
        timer.Cancel();
        User::WaitForRequest(timerStatus);
        break;
    } else if (err != KErrNone) {
        // OMITTED: error handling routine, not very important right now
    }

    // OMITTED: entry processing, adding to result QList

    resolver.Next(entry, err);
}
timer.Close();
resolver.Close();

这个代码有点有效。更重要的是,它的工作方式功能是正确的 - 超时起作用,返回到目前为止发现的设备,如果发现提前结束,那么它会退出而不等待计时器。问题是 - 它在程序中留下了一个杂散线程。这意味着,当我退出应用程序时,它的进程仍然在后台加载,什么也不做。我不是那种会对“修复”感到满意的程序员,比如让“退出”按钮终止进程而不是优雅地退出。留下杂散线程似乎是一个太严重的资源泄漏。

有什么办法可以解决这个问题吗?我不介意从头开始重写所有内容,即使使用完全不同的 API(只要我们谈论的是本机 Symbian API),我只是希望它能够工作。我已经阅读了一些有关活动对象的内容,但它似乎不是我所需要的,因为我只需要它同步工作......在发生较大变化的情况下,我将不胜感激更详细的解释,因为我Symbian C++ 新手,我真的不需要掌握它 - 这个小蓝牙模块可能是我在可预见的将来需要编写的所有内容。

预先感谢您的任何帮助! :)

I am writing an application in Qt to be deployed on Symbian S60 platform. Unfortunately, it needs to have Bluetooth functionality - nothing really advanced, just simple RFCOMM client socket and device discovery. To be exact, the application is expected to work on two platforms - Windows PC and aforementioned S60.

Of course, since Qt lacks Bluetooth support, it has to be coded in native API - Winsock2 on Windows and Symbian C++ on S60 - I'm coding a simple abstraction layer. And I have some problems with the discovery part on Symbian.

The discovery call in the abstraction layer should work synchronously - it blocks until the end of the discovery and returns all the devices as a QList. I don't have the exact code right now, but I had something like that:

RHostResolver resolver;
TInquirySockAddr addr;
// OMITTED: resolver and addr initialization

TRequestStatus err;
TNameEntry entry;
resolver.GetByAddress(addr, entry, err);
while (true) {
    User::WaitForRequest(err);
    if (err == KErrHostResNoMoreResults) {
       break;
    } else if (err != KErrNone) {
        // OMITTED: error handling routine, not very important right now
    }

    // OMITTED: entry processing, adding to result QList

    resolver.Next(entry, err);
}
resolver.Close();

Yes, I know that User::WaitForRequest is evil, that coding Symbian-like, I should use active objects, and so on. But it's just not what I need. I need a simple, synchronous way of doing device discovery.

And the code above does work. There's one quirk, however - I'd like to have a timeout during the discovery. That is, I want the discovery to take no more than, say, 15 seconds - parametrized in a function call. I tried to do something like this:

RTimer timer;
TRequestStatus timerStatus;
timer.CreateLocal();

RHostResolver resolver;
TInquirySockAddr addr;
// OMITTED: resolver and addr initialization

TRequestStatus err;
TNameEntry entry;

timer.After(timerStatus, timeout*1000000);

resolver.GetByAddress(addr, entry, err);
while (true) {
    User::WaitForRequest(err, timerStatus);

    if (timerStatus != KRequestPending) { // timeout
        resolver.Cancel();
        User::WaitForRequest(err);
        break;
    }

    if (err == KErrHostResNoMoreResults) {
        timer.Cancel();
        User::WaitForRequest(timerStatus);
        break;
    } else if (err != KErrNone) {
        // OMITTED: error handling routine, not very important right now
    }

    // OMITTED: entry processing, adding to result QList

    resolver.Next(entry, err);
}
timer.Close();
resolver.Close();

And this code kinda works. Even more, the way it works is functionally correct - the timeout works, the devices discovered so far are returned, and if the discovery ends earlier, then it exits without waiting for the timer. The problem is - it leaves a stray thread in the program. That means, when I exit my app, its process is still loaded in background, doing nothing. And I'm not the type of programmer who would be satisfied with a "fix" like making the "exit" button kill the process instead of exiting gracefully. Leaving a stray thread seems a too serious resource leak.

Is there any way to solve this? I don't mind rewriting everything from scratch, even using totally different APIs (as long as we're talking about native Symbian APIs), I just want it to work. I've read a bit about active objects, but it doesn't seem like what I need, since I just need this to work synchronously... In the case of bigger changes, I would appreciate more detailed explanations, since I'm new to Symbian C++, and I don't really need to master it - this little Bluetooth module is probably everything I'll need to write in it in foreseeable future.

Thanks in advance for any help! :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

北方的韩爷 2024-09-18 11:21:27

你的代码对我来说看起来没问题。您错过了不消耗您发出的所有请求的常见陷阱。假设您还取消计时器并在错误处理条件内执行 User::WaitForRequest(timerStatus) ,它应该可以工作。

我猜您担心的是主线程无法请求该线程退出。您可以大致按如下方式执行此操作:

  • 当主线程创建线程时,将指向 TRequestStatus 的指针传递到线程中。将此称为exitStatus
  • 当您执行User::WaitForRequest时,还要等待exitStatus
  • 当主线程希望子线程退出时,它会执行一个 bluetoothThread.RequestComplete(exitStatus, KErrCancel),其中 bluetoothThread 是该子线程退出的 RThread 对象。创建的主线程。
  • 在子线程中,当发出 exitStatus 信号时,退出循环以终止线程。您需要确保取消并使用计时器和蓝牙请求。
  • 主线程应该执行bluetoothThread.Logon并等待信号以等待蓝牙线程退出。

可能会有一些更微妙的地方来正确处理所有错误情况等等。

我希望我没有在这里咆哮错误的树......

The code you have looks ok to me. You've missed the usual pitfall of not consuming all the requests that you've issued. Assuming that you also cancel the timer and do a User::WaitForRequest(timerStatus) inside you're error handing condition, it should work.

I'm guessing that what you're worrying about is that there's no way for your main thread to request that this thread exit. You can do this roughly as follows:

  • Pass a pointer to a TRequestStatus into the thread when it is created by your main thread. Call this exitStatus.
  • When you do the User::WaitForRequest, also wait on exitStatus.
  • The main thread will do a bluetoothThread.RequestComplete(exitStatus, KErrCancel) when it wants the subthread to exit, where bluetoothThread is the RThread object that the main thread created.
  • in the subthread, when exitStatus is signalled, exit the loop to terminate the thread. You need to make sure you cancel and consume the timer and bluetooth requests.
  • the main thread should do a bluetoothThread.Logon and wait for the signal to wait for the bluetooth thread to exit.

There will likely be some more subtleties to deal correctly with all the error cases and so on.

I hope I'm not barking up the wrong tree altogether here...

随心而道 2024-09-18 11:21:27

问题已经得到解答,但是...如果您使用活动对象,我建议您使用嵌套活动调度程序(CActiveSchedulerWait 类)。然后,您可以将其传递给活动对象(用于计时器的 CPeriodic 和用于蓝牙的其他一些 CActive),其中之一将在其 RunL() 方法中停止此嵌套调度程序。不仅如此,通过这种方法,您的调用对于调用者来说变得同步,并且您的线程将在执行调用后正常关闭。

如果您对解决方案感兴趣,请搜索 CActiveSchedulerWait 的示例,或者直接询问我,我将为您提供一些代码示例。

The question is already answered, but... If you'd use active objects, I'd propose you to use nested active scheduler (class CActiveSchedulerWait). You could then pass it to your active objects (CPeriodic for timer and some other CActive for Bluetooth), and one of them would stop this nested scheduler in its RunL() method. More than this, with this approach your call becomes synchronous for the caller, and your thread will be gracefully closed after performing the call.

If you're interested in the solution, search for examples of CActiveSchedulerWait, or just ask me and I'll give your some code sample.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文