Xamarin-Android 和 UWP 之间的蓝牙连接

2024-02-29

在相当多的时间里,我一直在努力寻找一个可行的解决方案来通过 IP 连接或蓝牙连接 Android 设备和 UWP 应用程序(在 PC 上)。主要问题是找到一组足够简单但又保证可以工作的代码或示例(这样我的努力就不会白费,这种情况已经持续了一个多星期了)。

显而易见的是,不可能存在“代码对”(客户端-服务器),因为使用的库和构建代码结构的方式必须有很大不同。另一个问题是蓝牙似乎不允许环回连接,这会导致更多测试问题。另一个问题可能是过时的示例项目。而且很难找到xamarin/c#解决方案,而且我不想进入Android Studio和Java(我的项目是UWP项目,android部分只是用于测试)。这些对我来说实在是太多的困难了。

现在的目标(也是我的目标)question寻求帮助)是一个基本操作:

  • 从 Xamarin-Android(作为客户端)向 UWP(作为服务器)发送纯消息或数据流,并通过蓝牙接收响应。

现在让我们忽略设备搜索(如果可能的话),让我们直接使用 IP/MAC 地址。从那时起,一切都应该水到渠成。所有必要的功能/声明均已设置,设备也已配对。

我将非常感谢任何帮助。


我自己找到了解决方案,所以它是这样的:

首先,请记住定义蓝牙所有必要的声明和功能。这将明确关注代码部分。

对于 Xamarin/Android 客户端部分。该网站非常有帮助是这个吗 https://brianpeek.com/connect-to-a-bluetooth-device-with-xamarinandroid/。还可以尝试一下相当知名的聊天样本 https://developer.xamarin.com/samples/monodroid/BluetoothChat/对于 Xamarin.CreateMessage是一种在本地设备上创建可显示的调试消息的方法。我保持非常简单,因为我的项目主要是关于 UWP 部分。所有这些都包含在一个try { } catch { }子句,但由于有更多的缩进和括号,我现在将其省略。

using Java.Util;
using System.Text;
using System.IO;
using Android.Runtime;
using System.Threading.Tasks;

TestClass
{
    // The UUIDs will be displayed down below if not known.
    const string TARGET_UUID = "00001105-0000-1000-8000-00805f9b34fb";
    BluetoothSocket socket = null;
    OutputStreamInvoker outStream = null;
    InputStreamInvoker inStream = null;

    void Connect ()
    {
        BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
        if (adapter == null) CreateMessage ("No Bluetooth adapter found.");
        else if (!adapter.IsEnabled) CreateMessage ("Bluetooth adapter is not enabled.");

        List<BluetoothDevice> L = new List<BluetoothDevice> ();
        foreach (BluetoothDevice d in adapter.BondedDevices)
        {
            CreateMessage ("D: " + d.Name + " " + d.Address + " " + d.BondState.ToString ());
            L.Add (d);
        }

        BluetoothDevice device = null;
        device = L.Find (j => j.Name == "PC-NAME");

        if (device == null) CreateMessage ("Named device not found.");
        else
        {
            CreateMessage ("Device has been found: " + device.Name + " " + device.Address + " " + device.BondState.ToString ());
        }

        socket = device.CreateRfcommSocketToServiceRecord (UUID.FromString (TARGET_UUID));
        await socket.ConnectAsync ();

        if (socket != null && socket.IsConnected) CreateMessage ("Connection successful!");
        else CreateMessage ("Connection failed!");

        inStream = (InputStreamInvoker) socket.InputStream;
        outStream = (OutputStreamInvoker) socket.OutputStream;

        if (socket != null && socket.IsConnected)
        {
            Task t = new Task (() => Listen (inStream));
            t.Start ();
        }
        else throw new Exception ("Socket not existing or not connected.");
    }
}

现在我们进入字节和痛苦的部分。我使用这种格式来传输消息:[4 bytes of uint for message length] [1 byte per character]。重要的是您使用相同的字节到 uint 转换,因为字节顺序 https://en.wikipedia.org/wiki/Endianness或总体情况在 UWP 特定方法中存在差异。如果你的字长不是它应该的样子(而不是~23,比如3000000+),那就是一个问题。读取尚不存在的字节可能意味着异常,甚至无情的崩溃,尽管使用try { } catch { }条款。

以下方法以上述格式发送消息。如前所述,这是最简单的方法之一,所以我不会提及如何完成事情better.

private async void SendMessage (string message)
{
    uint messageLength = (uint) message.Length;
    byte[] countBuffer = BitConverter.GetBytes (messageLength);
    byte[] buffer = Encoding.UTF8.GetBytes (message);

    await outStream.WriteAsync (countBuffer, 0, countBuffer.Length);
    await outStream.WriteAsync (buffer, 0, buffer.Length);
}

用法:运行方法 1,然后运行方法 2。您还可以在方法 1 最后(当已连接时)执行 SendMessage。

现在到关于监听消息/响应的部分。在第一种方法中,您将看到这个方法是通过任务运行的,因此它不会阻止它启动的方法。也许有 Xamarin/Android 特定的方法可以解决这个问题,但这对我来说并不重要,所以我只是绕过了这个问题。

private async void Listen (Stream inStream)
{
    bool Listening = true;
    CreateMessage ("Listening has been started.");
    byte[] uintBuffer = new byte[sizeof (uint)]; // This reads the first 4 bytes which form an uint that indicates the length of the string message.
    byte[] textBuffer; // This will contain the string message.

    // Keep listening to the InputStream while connected.
    while (Listening)
    {
        try
        {
            // This one blocks until it gets 4 bytes.
            await inStream.ReadAsync (uintBuffer, 0, uintBuffer.Length);
            uint readLength = BitConverter.ToUInt32 (uintBuffer, 0);

            textBuffer = new byte[readLength];
            // Here we know for how many bytes we are looking for.
            await inStream.ReadAsync (textBuffer, 0, (int) readLength);

            string s = Encoding.UTF8.GetString (textBuffer);
            CreateMessage ("Received: " + s);
        }
        catch (Java.IO.IOException e)
        {
            CreateMessage ("Error: " + e.Message);
            Listening = false;
            break;
        }
    }
    CreateMessage ("Listening has ended.");
}

这只是工作的一半。对于 UWP 服务器部分,我将简单地发布我的current代码,更加干净,不需要编辑。

using System;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Bluetooth.Rfcomm;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;

namespace BT
{
    public sealed class BluetoothConnectionHandler
    {
        RfcommServiceProvider provider;
        bool isAdvertising = false;
        StreamSocket socket;
        StreamSocketListener socketListener;
        DataWriter writer;
        DataReader reader;
        Task listeningTask;

        public bool Listening { get; private set; }
        // I use Actions for transmitting the output and debug output. These are custom classes I created to pack them more conveniently and to be able to just "Trigger" them without checking anything. Replace this with regular Actions and use their invoke methods.
        public ActionSingle<string> MessageOutput { get; private set; } = new ActionSingle<string> ();
        public ActionSingle<string> LogOutput { get; private set; } = new ActionSingle<string> ();

        // These were in the samples.
        const uint SERVICE_VERSION_ATTRIBUTE_ID = 0x0300;
        const byte SERVICE_VERSION_ATTRIBUTE_TYPE = 0x0a; // UINT32
        const uint SERVICE_VERSION = 200;

        const bool DO_RESPONSE = true;

        public async void StartServer ()
        {
            // Initialize the provider for the hosted RFCOMM service.
            provider = await RfcommServiceProvider.CreateAsync (RfcommServiceId.ObexObjectPush);

            // Create a listener for this service and start listening.
            socketListener = new StreamSocketListener ();
            socketListener.ConnectionReceived += OnConnectionReceived;
            await socketListener.BindServiceNameAsync (provider.ServiceId.AsString (), SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);

            // Set the SDP attributes and start advertising.
            InitializeServiceSdpAttributes (provider);
            provider.StartAdvertising (socketListener);
            isAdvertising = true;
        }

        public void Disconnect ()
        {
            Listening = false;
            if (provider != null) { if (isAdvertising) provider.StopAdvertising (); provider = null; } // StopAdvertising relentlessly causes a crash if not advertising.
            if (socketListener != null) { socketListener.Dispose (); socketListener = null; }
            if (writer != null) { writer.DetachStream (); writer.Dispose (); writer = null; }
            if (reader != null) { reader.DetachStream (); reader.Dispose (); reader = null; }
            if (socket != null) { socket.Dispose (); socket = null; }
            if (listeningTask != null) { listeningTask = null; }
        }

        public async void SendMessage (string message)
        {
            // There's no need to send a zero length message.
            if (string.IsNullOrEmpty (message)) return;

            // Make sure that the connection is still up and there is a message to send.
            if (socket == null || writer == null) { LogOutput.Trigger ("Cannot send message: No clients connected."); return; } // "No clients connected, please wait for a client to connect before attempting to send a message."

            uint messageLength = (uint) message.Length;
            byte[] countBuffer = BitConverter.GetBytes (messageLength);
            byte[] buffer = Encoding.UTF8.GetBytes (message);

            LogOutput.Trigger ("Sending: " + message);

            writer.WriteBytes (countBuffer);
            writer.WriteBytes (buffer);

            await writer.StoreAsync ();
        }



        private void InitializeServiceSdpAttributes (RfcommServiceProvider provider)
        {
            DataWriter w = new DataWriter ();

            // First write the attribute type.
            w.WriteByte (SERVICE_VERSION_ATTRIBUTE_TYPE);

            // Then write the data.
            w.WriteUInt32 (SERVICE_VERSION);

            IBuffer data = w.DetachBuffer ();
            provider.SdpRawAttributes.Add (SERVICE_VERSION_ATTRIBUTE_ID, data);
        }

        private void OnConnectionReceived (StreamSocketListener listener, StreamSocketListenerConnectionReceivedEventArgs args)
        {
            provider.StopAdvertising ();
            isAdvertising = false;
            provider = null;
            listener.Dispose ();
            socket = args.Socket;
            writer = new DataWriter (socket.OutputStream);
            reader = new DataReader (socket.InputStream);
            writer.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
            reader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
            //StartListening ();
            LogOutput.Trigger ("Connection established.");
            listeningTask = new Task (() => StartListening ());
            listeningTask.Start ();
            // Notify connection received.
        }

        private async void StartListening ()
        {
            LogOutput.Trigger ("Starting to listen for input.");
            Listening = true;
            while (Listening)
            {
                try
                {
                    // Based on the protocol we've defined, the first uint is the size of the message. [UInt (4)] + [Message (1*n)] - The UInt describes the length of the message.
                    uint readLength = await reader.LoadAsync (sizeof (uint));

                    // Check if the size of the data is expected (otherwise the remote has already terminated the connection).
                    if (!Listening) break;
                    if (readLength < sizeof (uint))
                    {
                        Listening = false;
                        Disconnect ();
                        LogOutput.Trigger ("The connection has been terminated.");
                        break;
                    }

                    uint messageLength = reader.RReadUint (); // 

                    LogOutput.Trigger ("messageLength: " + messageLength.ToString ());

                    // Load the rest of the message since you already know the length of the data expected.
                    readLength = await reader.LoadAsync (messageLength);

                    // Check if the size of the data is expected (otherwise the remote has already terminated the connection).
                    if (!Listening) break;
                    if (readLength < messageLength)
                    {
                        Listening = false;
                        Disconnect ();
                        LogOutput.Trigger ("The connection has been terminated.");
                        break;
                    }

                    string message = reader.ReadString (messageLength);
                    MessageOutput.Trigger ("Received message: " + message);
                    if (DO_RESPONSE) SendMessage ("abcdefghij");
                }
                catch (Exception e)
                {
                    // If this is an unknown status it means that the error is fatal and retry will likely fail.
                    if (SocketError.GetStatus (e.HResult) == SocketErrorStatus.Unknown)
                    {
                        Listening = false;
                        Disconnect ();
                        LogOutput.Trigger ("Fatal unknown error occurred.");
                        break;
                    }
                }
            }
            LogOutput.Trigger ("Stopped to listen for input.");
        }
    }
}

用法如下:

  1. 创建BluetoothConnectionHandler 的实例。
  2. 设置 MessageOutput 和/或 LogOutput(阅读代码中与此相关的注释)。
  3. 运行其 StartServer 方法。
  4. 要发送消息,请使用其 SendMessage 方法。

RReadUint的扩展方法:

public static uint RReadUint (this DataReader reader)
{
    uint a = 0;
    byte[] x = new byte[sizeof (uint)];
    reader.ReadBytes (x);
    a = BitConverter.ToUInt32 (x, 0);
    return a;
}

这应该包含完成我要求的一切所需的一切......事后看来,我发现没有简单的答案可能。从现在开始一切可以得到改进,因为它可能是 UWP 和 Xamarin/Android 之间进行蓝牙通信的最基本方式。

如果您对此有疑问,请随时在评论部分提问。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Xamarin-Android 和 UWP 之间的蓝牙连接 的相关文章

  • 选择列表逻辑应位于 ASP.NET MVC、视图、模型或控制器中的什么位置?

    我觉得我的问题与这个问题很接近 但我想对这样的代码应该放在哪里进行更一般的讨论 Asp Net MVC SelectList 重构问题 https stackoverflow com questions 2149855 asp net mv
  • 如何使用MySqlCommand和prepare语句进行多行插入?(#C)

    Mysql 给出了如何使用准备语句和 NET 插入行的示例 http dev mysql com doc refman 5 5 en connector net programming prepared html http dev mysq
  • 获取尚未实例化的类的函数句柄

    我对 C 相当陌生 我想做的事情可能看起来很复杂 首先 我想获取一些函数的句柄以便稍后执行它们 我知道我可以通过以下方式实现这一目标 List
  • 带 If 的嵌套 For 循环的时间复杂度

    void f int n for int i 1 i lt n i if i int sqrt n 0 for int k 0 k lt pow i 3 k do something 我的思考过程 执行if语句的次数 sum i 1 to
  • Android 中 Activity 之间的 3D 动画

    How to create animation between two Activity look like As Screen shot in android 搜索jazzyviewpager 这是link https github co
  • C++11 动态线程池

    最近 我一直在尝试寻找一个用于线程并发任务的库 理想情况下 是一个在线程上调用函数的简单接口 任何时候都有 n 个线程 有些线程比其他线程完成得更快 并且到达的时间不同 首先我尝试了 Rx 它在 C 中非常棒 我还研究了 Blocks 和
  • C# 中的常量和只读? [复制]

    这个问题在这里已经有答案了 可能的重复 const 和 readonly 之间有什么区别 https stackoverflow com questions 55984 what is the difference between cons
  • tabcontrol selectedindex 更改事件未被触发 C#

    嘿伙计们 我有一个很小的问题 请参阅下面的代码 this is main load private void Form1 Load object sender EventArgs e tabAddRemoveOperator Selecte
  • .NET 客户端中 Google 表格中的条件格式请求

    我知道如何在 Google Sheets API 中对值和其他格式进行批量电子表格更新请求 但条件格式似乎有所不同 我已正确设置请求 AddConditionalFormatRuleRequest formatRequest new Add
  • 调试android数据绑定?

    谁能告诉我如何调试或找到数据绑定生成的代码 从this https www youtube com watch v NBbeQMOcnZ0链接我发现它生成了所需的代码 我猜您正在寻找自动生成的绑定 java 文件 我也在寻找他们 最后我在这
  • 使用 mingw32 在 Windows 上构建 glew 时“DllMainCRTStartup@12”的多个定义

    我关注了这个主题 使用 mingw 使建筑物在 Windows 上闪闪发光 https stackoverflow com questions 6005076 building glew on windows with mingw 6005
  • 0-1背包算法

    以下 0 1 背包问题是否可解 浮动 正值和 浮动 权重 可以是正数或负数 背包的 浮动 容量 gt 0 我平均有 这是一个相对简单的二进制程序 我建议用蛮力进行修剪 如果任何时候你超过了允许的重量 你不需要尝试其他物品的组合 你可以丢弃整
  • 使用 iTextSharp 5.3.3 和 USB 令牌签署 PDF

    我是 iTextSharp 和 StackOverFlow 的新手 我正在尝试使用外部 USB 令牌在 C 中签署 PDF 我尝试使用从互联网上挖掘的以下代码 Org BouncyCastle X509 X509CertificatePar
  • Visual Studio 2017 完全支持 C99 吗?

    Visual Studio 的最新版本改进了对 C99 的支持 最新版本VS2017现在支持所有C99吗 如果没有 C99 还缺少哪些功能 No https learn microsoft com en us cpp visual cpp
  • 为什么 Linux 对目录使用 getdents() 而不是 read()?

    我浏览 K R C 时注意到 为了读取目录中的条目 他们使用了 while read dp gt fd char dirbuf sizeof dirbuf sizeof dirbuf code Where dirbuf是系统特定的目录结构
  • C语言声明数组没有初始大小

    编写一个程序来操纵温度详细信息 如下所示 输入要计算的天数 主功能 输入摄氏度温度 输入功能 将温度从摄氏度转换为华氏度 独立功能 查找华氏度的平均温度 我怎样才能在没有数组初始大小的情况下制作这个程序 include
  • 受限 AppDomain 中的代码访问安全异常

    Goal 我需要在权限非常有限的 AppDomain 中运行一些代码 它不应该访问任何花哨或不安全的内容 except对于我在其他地方定义的一些辅助方法 我做了什么 我正在创建一个具有所需基本权限的沙箱 AppDomain 并创建一个运行代
  • 我想测量 ListView 的高度 (getHight() = 0)

    我无法自己决定任务将是问 我想测量 ListView 的高度 无法捕捉渲染 ListView 的时刻 rssListView getHight 0 public class RSSactivity extends Activity publ
  • 带有私有设置器的 EFCore Base 实体模型属性 - 迁移奇怪的行为

    实体模型继承的类内的私有设置器似乎会导致 EFCore 迁移出现奇怪的问题 考虑以下示例 其中有多个类 Bar and Baz 继承自Foo 跑步时Add Migration多次命令 添加 删除private修饰符 生成的模式在多个方面都是
  • Android 的代码覆盖率[重复]

    这个问题在这里已经有答案了 可能的重复 Android测试代码覆盖率 Eclipse https stackoverflow com questions 3282702 android test code coverage eclipse

随机推荐

  • PHP 中有一个像样的 HTML-table-generator 类吗? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在 PHP 中寻找一个像样的类 它将生成复杂的 HTML 表格 即它应该支持 colspan rowspan 以及行 列和单元格的单独
  • 身份验证类型名称如何在 asp.net vnext 中注册

    因此 我正在更新 MongoDB 的开源 asp net 身份提供程序 以便与 Asp Net Identity 3 0 又名 vnext 配合使用 到目前为止 我已经能够注册提供程序并创建用户 但是在使用 SignInManager 时
  • 从缓存对象中转换 ValueTuple

    当我检查缓存以查看我的 ValueTuple 是否在缓存中时 我使用下面的代码 在这种情况下 返回的值为 null 即缓存中不存在 当代码运行时我得到object not set to instance of an object第一行错误
  • 如何测试 AccessViolationException 的处理

    我需要编写一个测试来验证我的代码是否可以处理 AccessViolationException 或任何其他 WIN32 损坏状态异常 CSE 该异常在不安全的上下文中发生 通常通过调用第 3 方库来处理 这一切都应该在 net 4 0 上使
  • 如何在 IPython 笔记本中播放本地视频?

    我有一个本地视频文件 avi 但可以转换 我想向客户端展示该文件 即它是私有的 无法发布到网络 但我不知道如何在 IPython 笔记本中播放它 经过一番谷歌搜索后 似乎 HTML5 视频标签可能是可行的方法 但我不知道任何 html 无法
  • 部署应用程序时如何避免安装“无限强度”JCE策略文件?

    我有一个使用 256 位 AES 加密的应用程序 Java 不支持该加密 我知道为了让它正常工作 我在安全文件夹中安装了 JCE 无限强度 jar 这对我作为开发人员来说很好 我可以安装它们 我的问题是 由于此应用程序将被分发 最终用户很可
  • 获取我在 Sql Server 中更新的行的 Id

    我正在尝试返回在 sql 中更新的行的 Id UPDATE ITS2 UserNames SET AupIp AupIp WHERE Customer ID TCID AND Handle ID ID SELECT ERROR AS Err
  • 为什么第一个 WCF 客户端调用很慢?

    我试图弄清楚为什么客户端应用程序启动后的第一个 WCF 调用比第二个调用花费更多的时间 我做了什么来测试 实现了简单的自托管 WCF 服务器和控制台客户端 服务器已预热 在运行测试之前我运行它并调用方法几次 绑定是basicHttpBind
  • 重新启动 erlang 进程并保留状态

    我有一个主管进程 它启动多个子进程 目前 当孩子死亡时 我会生成一个具有新 Pid 的新进程 这意味着我丢失了刚刚死亡的子进程的状态信息 我希望我的客户始终使用相同的标识符与子进程进行通信 尽管子进程可能会死亡并由主管重新启动 我正在考虑使
  • 无法将类型“UnsafePointer”的值转换为预期参数类型“UnsafePointer<_>”

    我正在 OS X 的 Swift 中使用外部 C 库 我得到一个值 cda 它在 C 中定义为double 它是一个指向双数组的指针 当导入到 Swift 中时 它会将类型识别为 UnsafeMutablePointer 我正在尝试将此指针
  • 如何在 mpandroidchart 中设置 x 标签和 y 标签的值

    我正在使用 mpandroidchart android 库 我正在实施折线图 这里我可以自己设置x和y标签吗 目前 它正在根据提供给图表的数据集添加值 您能对此提供一些想法吗 您必须在轴对象上使用格式化程序 有两种格式化程序 XAxisV
  • 带有 NULL 的列不能用于 Bigquery 中的联合

    我正在工作中构建动态 sql 生成器 并且在联合查询方面遇到困难 当我运行以下查询时 SELECT NULL AS field 列类型是BOOL默认情况下 它可以用于与整数类型列的联合 如下所示 SELECT field FROM SELE
  • 如何以编程方式“重新启动”Android 应用程序?

    首先 我知道不应该真正终止 重新启动 Android 上的应用程序 在我的用例中 我想在服务器向客户端发送一条特定信息的特定情况下对我的应用程序进行出厂重置 用户只能使用应用程序的一个实例登录服务器 即不允许使用多个设备 如果另一个实例获得
  • 致命错误:在非对象 codeigniter $query->num_rows()==1) 上调用成员函数 where() [重复]

    这个问题在这里已经有答案了 可能的重复 CodeIgniter 在非对象上调用成员函数 select https stackoverflow com questions 8322124 codeigniter call to a membe
  • 在 CQRS http 应用程序中实现 Saga/Process Manager

    按照这个例子 https msdn microsoft com en us library jj591569 aspx https msdn microsoft com en us library jj591569 aspx 图3 它如何适
  • ofstream 无法在 Linux 上运行

    我有一个简单的测试代码 include
  • 递归排列列表

    我想通过递归来解决问题 我有一个给定的字母列表 我想要这些字母的所有排列 直到达到给定的长度 var letters A B C D E F function myRekursion alphabet n if n gt 0 for var
  • 高效利用Android资源

    我试图弄清楚如何有效地使用 Android 的资源系统 既提供最高分辨率的资源 又保持较小的 apk 大小 我的资源文件夹 drawable normal mdpi drawable normal hdpi drawable large m
  • 使用 OpenMP 在两个内核上设置线程关联

    我使用的是C程序 在Windows7上用gcc 4 9 2编译 使用OpenMP 4 0 我的电脑是双核 四个线程 我想使用线程亲和力传播并使用放置在不同核心上的 2 个线程 因此 当我从 DOS 设置环境变量时 设置 OMP NUM TH
  • Xamarin-Android 和 UWP 之间的蓝牙连接

    在相当多的时间里 我一直在努力寻找一个可行的解决方案来通过 IP 连接或蓝牙连接 Android 设备和 UWP 应用程序 在 PC 上 主要问题是找到一组足够简单但又保证可以工作的代码或示例 这样我的努力就不会白费 这种情况已经持续了一个