win10 uwp应用开发(JS)——蓝牙Rfcomm连接

1.环境

操作系统名称	Microsoft Windows 10 专业版
版本	10.0.15063 版本 15063
Microsoft Visual Studio Community 2017 
版本 15.3.3
VisualStudio.15.Release/15.3.3+26730.12
Microsoft .NET Framework
版本 4.7.02046

已安装的版本: Community

Visual Basic 2017   00369-60000-00001-AA520
Microsoft Visual Basic 2017

Visual C# 2017   00369-60000-00001-AA520
Microsoft Visual C# 2017

Visual C++ 2017   00369-60000-00001-AA520
Microsoft Visual C++ 2017

Application Insights Tools for Visual Studio 包   8.8.00712.1
Application Insights Tools for Visual Studio

ASP.NET and Web Tools 2017   15.0.30726.0
ASP.NET and Web Tools 2017

ASP.NET Core Razor Language Services   1.0
Provides languages services for ASP.NET Core Razor.

ASP.NET 模板引擎 2017   15.0.30726.0
ASP.NET 模板引擎 2017

JavaScript Project System   2.0
JavaScript Project System

JavaScript UWP 项目系统   2.0
JavaScript UWP 项目系统

JavaScript 语言服务   2.0
JavaScript 语言服务

Microsoft JVM Debugger   1.0
Provides support for connecting the Visual Studio debugger to JDWP compatible Java Virtual Machines

Microsoft MI-Based Debugger   1.0
Provides support for connecting Visual Studio to MI compatible debuggers

Microsoft Visual C++ 向导   1.0
Microsoft Visual C++ 向导

Microsoft Visual Studio VC 软件包   1.0
Microsoft Visual Studio VC 软件包

NuGet 包管理器   4.3.1
Visual Studio 中的 NuGet 包管理器。有关 NuGet 的详细信息,请访问 http://docs.nuget.org/。

TypeScript   2.3.4.0
用于 Visual Studio 的 TypeScript 工具

Visual Studio Code Debug Adapter Host Package   1.0
Interop layer for hosting Visual Studio Code debug adapters in Visual Studio

Visual Studio tools for CMake   1.0
Visual Studio tools for CMake

Visual Studio Tools for Universal Windows Apps   15.0.26730.08
Visual Studio Tools for Universal Windows Apps 让你能够构建一种通用的应用体验,运行 Windows 10 的每种设备都可以获得这种体验,如手机、平板电脑、PC 等。它包含 Microsoft Windows 10 软件开发工具包。

通用 Azure 工具   1.10
通过 Azure 移动服务和 Microsoft Azure Tools 提供通用服务。

2.参考资料和API

蓝牙开发MSDN资料 https://docs.microsoft.com/zh-cn/windows/uwp/devices-sensors/bluetooth

主要涉及的包

//导入包
let { BluetoothCacheMode, BluetoothDevice } = Windows.Devices.Bluetooth;
let { RfcommDeviceService, RfcommServiceId } = Windows.Devices.Bluetooth.Rfcomm;
let { DeviceInformation } = Windows.Devices.Enumeration;
let { StreamSocket, SocketProtectionLevel } = Windows.Networking.Sockets;
let { DataWriter, DataReader, UnicodeEncoding } = Windows.Storage.Streams;
/**
 * 一些常量
 */
const Constants = {
    RfcommServiceUuid: RfcommServiceId.fromUuid("58517d84-357d-c58a-0000-000000000000"),
    //  RfcommServiceUuid: RfcommServiceId.obexObjectPush,
    SdpServiceNameAttributeId: 0x100,
    SdpServiceNameAttributeType: (4 << 3) | 5,
    SdpServiceName: "My Win1o BLE"
};

3.Rfcomm服务监听

 创建RfcommServiceProvider

provider = await RfcommServiceProvider.createAsync(Constants.RfcommServiceUuid);

使用StreamSocketListener监听socket连接,并且启动监听

listener = new StreamSocketListener();
    listener.addEventListener("connectionreceived", onConnectionreceived);
  await listener.bindServiceNameAsync(provider.serviceId.asString(), SocketProtectionLevel.bluetoothEncryptionAllowNullAuthentication);
provider.startAdvertising(listener, true);

 监听连接客户端的socket连接

/**
 * 监听客户端连接
 * @param {Windows.Networking.Sockets.StreamSocketListenerConnectionReceivedEventArgs} evt
 */
async function onConnectionreceived(evt)
{
    console.log(evt);
    let socket;
    try
    {
        socket = evt.socket;
    } catch (e)
    {
        console.log(e);
        disconnect();
        return;
    }

    console.log("socket连接", socket);
    let information = socket.information;
    info(information.remoteAddress + "  正在接入...");
    let remoteDevice = await BluetoothDevice.fromHostNameAsync(information.remoteHostName);//获取实际设备名
    console.log(remoteDevice);
    let holder = clients[information.remoteAddress];
    if (holder == null)//添加元素
    {
        holder = {};
        let ele = document.createElement("div");
        ele.className = "clientItem";
        ele.innerHTML = "<span>" + remoteDevice.name + "</span><span>" + information.remoteAddress + "</span><input  type=\"checkbox\" />";
        clientList.appendChild(ele);

        holder.socket = socket;
        holder.ele = ele;
        holder.name = remoteDevice.name;
        Object.defineProperty(holder, "isSelected", {
            get:
            function ()
            {
                return ele.querySelector("input").checked;
            }
        });
        holder.writer = new DataWriter(socket.outputStream);
        clients[information.remoteAddress] = holder;
    } else//更新元素
    {
        //do noting
    }
    let reader = new DataReader(socket.inputStream);
    //断开异常处理
    let errFun = function (igr)
    {
        console.log(igr);
        //断开连接
        info(holder.name + "断开连接..");
        delete clients[information.remoteAddress];
        clientList.removeChild(holder.ele);
    };
    //迭代循环读取
    function run()
    {
        reader.loadAsync(4).then(len =>
        {
            let size = reader.readInt32();//抛出异常处理 
            return size;
        }).then(size =>
        {
            console.log(size);
            return reader.loadAsync(size);
        }).then(textLen =>
        {
            console.log("文本长度:" + textLen);
            let str = reader.readString(textLen);
            return str;
        }).then(str =>
        {
            console.log(str);
            info(holder.name, str);
            run();
        }, errFun);
    }
    run();
}

 向连接的客户端发送消息

sendBtn.onclick = function ()
{
    let msg = sendText.value;
    console.log("发送文本", msg);
    info("服务器", msg);
    for (mac in clients)
    {
        let holder = clients[mac];
        if (holder.isSelected)
        {
            let writer = holder.writer;
            sendMsg(writer, msg);
        }
    }
}
/**
 * 
 * @param {Windows.Storage.Streams.DataWriter} writer
 * @param {string} msg
 */
function sendMsg(writer, msg)
{
    let textLen = writer.measureString(msg);
    console.log("写入长度:" + textLen);
    writer.writeInt32(textLen);
    writer.writeString(msg);
    writer.storeAsync();
}

 断开服务

function disconnect()
{
    startBtn.removeAttribute("disabled");
    startBtn.checked = false;

    for (mac in clients)
    {
        let holder = clients[mac];
        holder.socket.close();
    }
    if (listener != null)
    {
        info("服务已经断开");

        listener.close();
        listener = null;
    }
    if (provider != null)
    {
        provider.stopAdvertising();
        provider = null;
    }
}

4.Rfcomm连接服务

查找蓝牙设备

async function search()
{
    console.clear();
    connectBtn.setAttribute("disabled", true);

    //  RfcommDeviceService.getDeviceSelector.getDeviceSelector(Constants.RfcommServiceUuid);
    let selector = BluetoothDevice.getDeviceSelector();
    console.log("selector", selector);
    let devices = await DeviceInformation.findAllAsync(selector);
    console.log("devices", devices);
    info("正在搜索周边设备...");
    let bluetoothDevices = new Map();
    for (const device of devices)
    {
        let bluetoothDevice = await BluetoothDevice.fromIdAsync(device.id);
        if (bluetoothDevice != null)
        {
            //console.log(bluetoothDevice);
            bluetoothDevices.set(bluetoothDevice.deviceId, bluetoothDevice);
        }
    }
    console.log(bluetoothDevices);
    info("找到周边设备有" + bluetoothDevices.size + "个");

    notifyDataSetChanged(bluetoothDevices);

}

需要注意

DeviceInformation.findAllAsync(selector)

得到并一定全是BluetoothDevice或RfcommDeviceService,(至少js的api是这样,bug?),需要

let bluetoothDevice = await BluetoothDevice.fromIdAsync(device.id);

判断是否为蓝牙设备

检索蓝牙服务,通过bluetoothDevice.rfcommServices获取缓存结果

bluetoothDevice.getRfcommServicesAsync(BluetoothCacheMode.uncached).then(result =>
            {
                console.log("返回服务", result);
                for (const rfcommService of bluetoothDevice.rfcommServices)
                {
                    let inputDiv = document.createElement("div");
                    inputDiv.className = "itemLabel";
                    let inputEle = document.createElement("label");
                    inputEle.innerHTML = rfcommService.serviceId.asString() + "<input name='rfcommService' type='radio' />";
                    inputDiv.appendChild(inputEle);
                    itemDiv.appendChild(inputDiv);

                    inputDiv.onclick = function ()
                    {
                        clientList.className = "left";
                        msgText.className = "right";
                        connect(rfcommService);
                    };
                }
            });

 连接指定的服务

/**
 * 连接到指定服务
 * @param {Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService} rfcommService
 */
async function connect(rfcommService)
{
    console.log(rfcommService);
    connectText.textContent = "已连接到" + rfcommService.device.name + rfcommService.device.hostName;
    info("正在连接到 " + rfcommService.serviceId.uuid + " ...");
    socket = new StreamSocket();
    try
    {
        await socket.connectAsync(rfcommService.connectionHostName, rfcommService.connectionServiceName, SocketProtectionLevel.bluetoothEncryptionAllowNullAuthentication);
        info("连接成功");
    } catch (e)
    {
        console.log(e);
        error("不能连接到服务端!");
        connectText.textContent = "搜索设备";
        connectBtn.checked = false;
        socket = null;
        return;
    }
    writer = new DataWriter(socket.outputStream);
    let reader = new DataReader(socket.inputStream);
    //迭代循环读取
    function run()
    {
        reader.loadAsync(4).then(len =>
        {
            let size = reader.readInt32();//抛出异常处理 
            return size;
        }).then(size =>
        {
            console.log(size);
            return reader.loadAsync(size);
        }).then(textLen =>
        {
            console.log("文本长度:" + textLen);
            let str = reader.readString(textLen);
            return str;
        }).then(str =>
        {
            console.log(str);
            info("服务器", str);
            run();
        }, err =>
            {
                console.log(err);
                disconnect();
            });
    }
    run();
}

5.win10与Android的Rfcomm蓝牙通信对接

在win10中,设置 -> 设备 -> 蓝牙和其他设备 -> 切换 蓝牙 开关, 点击 添加蓝牙或其他设备 -> 蓝牙 ,需要 匹配Android蓝牙设备 才能正常通信

使用Android作为客户端,win10服务端;

 
win10 uwp应用开发(JS)——蓝牙Rfcomm连接
 
win10 uwp应用开发(JS)——蓝牙Rfcomm连接
 

或者Android作为服务端,或者Win10为客户端

 
win10 uwp应用开发(JS)——蓝牙Rfcomm连接
 

 
win10 uwp应用开发(JS)——蓝牙Rfcomm连接
 

6.源码

win10 源码:https://github.com/zhhaogen/BuletoothTest-UWP.git

android 源码:https://github.com/zhhaogen/BuletoothTest-Android.git

相关推荐