Windows Phone 8 蓝牙编程

蓝牙是手机的近距离无限传输的技术,在之前的Windows Phone 7系统手机里面仅支持蓝牙耳机功能,并不支持蓝牙文件信息传输,那么在Windows Phone 8手机里面将全面支持蓝牙技术,并且提供了相关的API来给开发者使用。开发者可以利用蓝牙的相关API来创建应用程序,在应用程序里面使用手机的蓝牙技术来进行近距离的文件传输和发送接收消息,创造出更加有趣和方便的应用软件。

Windows Phone 8里面可以在应用程序里面利用蓝牙进行通信,使用蓝牙相关的API,可以让应用程序连接到另外的一个应用程序,也可以让应用程序连接到一个设备上。Windows Phone 8的蓝牙技术支持两个蓝牙方案:一个是应用程序到应用程序的通信,另外一个是应用程序到设备的通信。

1.应用程序到应用程序的通信

应用程序到应用程序的通信的过程是,应用程序使用蓝牙去查找正在广播蓝牙服务的对等的应用程序,如果在应用程序提供服务的范围内发现一个应用程序,那么该应用程序可以发起连接请求。当这两个应用程序接受连接,它们之间就可以进行通信了,通信的过程是使用socket的消息发送接收机制。在Windows Phone 8中使用到应用程序到应用程序的蓝牙通讯技术,需要在项目的WMAppManifest.xml文件中添加ID_CAP_PROXIMITY的功能选项,表示支持临近的设备通信能力,否则程序会出现异常。

2.应用程序到设备的通信

在应用程序到设备的通信过程时,应用程序使用蓝牙去查找提供服务的设备,如果提供的服务范围之内发现一个可以连接的蓝牙设备,那么该应用程序可以发起连接请求。当应用程序和设备同时接受该连接,它们之间就可以进行通信了,通信的过程也是使用socket的消息发送接收机制,类似于应用程序到应用程序的通信。在Windows Phone 8中使用到应用程序到设备的蓝牙通讯技术,需要在项目的WMAppManifest.xml文件中添加ID_CAP_PROXIMITY和ID_CAP_NETWORKING的功能选项,表示支持临近的设备通信能力和网络通信能力,否则程序会出现异常。

蓝牙编程类

在Windows Phone 8里面使用到蓝牙编程主要会用到PeerFinder类,PeerInformation类,StreamSocket类和ConnectionRequestedEventArgs类,这些类的说明如表19.1所示。因为蓝牙也是基于TCP协议进行消息传递了,所以需要用到Socket的相关的编程知识,以及StreamSocket类。PeerFinder类是蓝牙查找类,它的主要成员如表19.2所示。

表19.1 蓝牙编程类的说明


类名


说明


PeerFinder


用于去查找附近的设备是否有运行和当前应用程序相同的应用程序,并且可以在两个应用程序之间建立起socket连接,从而可以进行通信。对等应用程序是在其他设备上运行的应用程序的另一个实例。


PeerInformation


包含对等应用程序或设备的识别信息。


StreamSocket


支持使用一个TCP的Socket流的网络通信。


ConnectionRequestedEventArgs


表示传递到一个应用程序的ConnectionRequested事件的属性

表 19.2 PeerFinder类的成员


成员


说明


bool AllowBluetooth


指定 PeerFinder 类的此实例是否可以通过使用 Bluetooth 来连接 ProximityStreamSocket 对象。如果PeerFinder 的此实例可以通过使用 Bluetooth 来连接 ProximityStreamSocket 对象,则为 true;否则为false。默认为 true。


bool AllowInfrastructure


是否使用TCP/IP协议连接到StreamSocket


bool AllowWiFiDirect


指定 PeerFinder 类的此实例是否可以通过使用 Wi-Fi Direct 来连接 ProximityStreamSocket 对象。如果 PeerFinder 的此实例可以通过使用 Wi-Fi Direct 来连接 ProximityStreamSocket 对象,则为 true;否则为false。默认为 true。


IDictionary<string, string> AlternateIdentities


获取要与其他平台上的对等应用程序匹配的备用 AppId 值列表。返回要与其他平台的对等类应用程序匹配的备用 AppId 值列表。


string DisplayName


获取或设置标识计算机到远程对等类的名称。


PeerDiscoveryTypes SupportedDiscoveryTypes


获取一个值,该值指示哪些发现选项可与 PeerFinder 类一同使用


event TypedEventHandler<object, ConnectionRequestedEventArgs> ConnectionRequested


远程对等类使用 ConnectAsync 方法请求连接时发生。


event TypedEventHandler<object, TriggeredConnectionStateChangedEventArgs> TriggeredConnectionStateChanged


在远程对等类的轻击笔势期间发生。


IAsyncOperation< StreamSocket> ConnectAsync(PeerInformation peerInformation)


连接已发现了对 FindAllPeersAsync 方法的调用的对等类。peerInformation:表示连接到的对等类的对等类信息对象。返回通过使用所提供的临近StreamSocket 对象连接远程对等类的异步操作。


IAsyncOperation<IReadOnlyList<PeerInformation>> FindAllPeersAsync()


适用于无线范围内运行相同应用程序的对等计算机的异步浏览。返回通过使用 Wi-Fi直连技术浏览对等类的异步操作。


void Start(string peerMessage)


向临近设备上的对等类应用程序传递消息。


void Stop()


停止查找对等类应用程序或广播对等类连接的过程

查找蓝牙设备和对等项

查找在服务范围内的蓝牙设备和对等项是蓝牙编程的第一步,查找蓝牙设备和对等项中会使用到PeerFinder类的FindAllPeersAsync方法去进行查找,然后以异步的方式返回查找到的对等项列表的信息IReadOnlyList<PeerInformation>,注意要使查找对等的应用程序时,在调用FindAllPeersAsync方法前必须先调用PeerFinder类的Start方法,主要的目的是启动广播服务,让对方的应用程序也能查找到自己。PeerInformation包含三个属性:一个是DisplayName表示对等项的名字,这个名字一般都是由对方的设备的名称或者查找到的应用程序自身设置的现实名字,一个是HostName表示主机名字或者IP地址,还有一个属性是ServiceName表示服务名称或者TCP协议的端口号。然后可以利用查找到的PeerInformation信息进行连接和通信。

查找对等的应用程序的代码示例:

async void AppToApp(){    // 启动查找服务    PeerFinder.Start();    //开始查找    ObservableCollection<PeerInformation> peers = await PeerFinder.FindAllPeersAsync();    if (peers.Count == 0)    {        //未找到任何的对等项    }    else    {        //处理查找到的对等项,可以使用PeerFinder类的ConnectAsync方法来连接选择的要进行通信的对等项    }}

查找蓝牙设备的代码示例:

private async void AppToDevice(){    // 设置查找所匹配的蓝牙设备    PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";    // 开始查找    ObservableCollection<PeerInformation> pairedDevices = await PeerFinder.FindAllPeersAsync();    if (pairedDevices.Count == 0)    {        // 没有找到可用的蓝牙设备    }    else    {        //处理查找到的蓝牙设备,可以新建一个StreamSocket对象,然后使用StreamSocket类的ConnectAsync方法通过HostName和ServiceName来连接蓝牙设备    }}

蓝牙发送消息

蓝牙编程的发送消息机制使用的是TCP的StreamSocket的方式,原理与Socket的一致。在蓝牙连接成功后,可以获取到一个StreamSocket类的对象,然后我们使用该对象的OutputStream属性来初始化一个DataWriter对象,通过DataWriter对象来进行发送消息。OutputStream属性表示的是Socket的输出流,用于发送消息给对方。下面来看一下发送消息的示例:

async void SendMessage(string message){    // 连接选中的对等项,selectedPeer为查找到的PeerInformation对象    StreamSocket _socket= = await PeerFinder.ConnectAsync(selectedPeer);    // 创建DataWriter    DataWriter _dataWriter = new DataWriter(_socket.OutputStream);    // 先写入发送消息的长度     _dataWriter.WriteInt32(message.Length);     await _dataWriter.StoreAsync();    // 最后写入发送消息的内容     _dataWriter.WriteString(message);     await _dataWriter.StoreAsync();}

蓝牙接收消息

蓝牙编程的接收消息机制同样也是使用的是TCP的StreamSocket的方式,原理与Socket的一致。在蓝牙连接成功后,可以获取到一个StreamSocket类的对象,然后我们使用该对象的InputStream属性来初始化一个DataReader对象,通过DataReader对象来进行接收消息。InputStream属性表示的是Socket的输入流,用于接收对方的消息。下面来看一下接收消息的示例:

async Task<string> GetMessage(){    // 连接选中的对等项,selectedPeer为查找到的PeerInformation对象    StreamSocket _socket= = await PeerFinder.ConnectAsync(selectedPeer);    // 创建DataReader    DataReader _dataReader = new DataReader(_socket.InputStream);    // 先读取消息的长度    await _dataReader.LoadAsync(4);    uint messageLen = (uint)_dataReader.ReadInt32();    // 最后读取消息的内容    await _dataReader.LoadAsync(messageLen);    return _dataReader.ReadString(messageLen); }

实例:实现蓝牙程序对程序的传输

下面给出蓝牙程序对程序传输的示例:通过使用蓝牙功能查找周边也要使用改应用的手机,互相建立起连接和发送测试消息。
代码清单19-1:蓝牙程序对程序传输(源代码:第19章/Examples_19_1)

MainPage.xaml文件主要代码

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">          <StackPanel>            <Button x:Name="btFindBluetooth" Content="通过蓝牙查找该应用设备" Click="btFindBluetooth_Click"/>            <ListBox x:Name="lbBluetoothApp"  ItemsSource="{Binding}" >             
<ListBox.ItemTemplate
>                <DataTemplate>                  <StackPanel>                    <TextBlock  Text="{Binding DisplayName}"
/>                    <TextBlock  Text="{Binding ServiceName}"
/>                         <Button Content="连接" HorizontalAlignment="Left"
Width="308"
Height="91"
Click="btConnect_Click"/>                  </StackPanel>                </DataTemplate>              </ListBox.ItemTemplate>            </ListBox>          </StackPanel>        </Grid>

MainPage.xaml.cs文件主要代码

using System;using System.Windows;using System.Windows.Controls;using Microsoft.Phone.Controls;using Windows.Networking.Proximity;using Windows.Networking.Sockets;using Windows.Storage.Streams;namespace BluetoothDemo{    public
partial class MainPage : PhoneApplicationPage    {       
private StreamSocket _socket = null;  // Socket数据流对象       
private DataWriter _dataWriter;  // 数据写入对象       
private DataReader _dataReader; // 数据读取对象        public
MainPage()       
{           
InitializeComponent();                 
Loaded += MainPage_Loaded;//页面加载事件        }     
  // 查找蓝牙对等项按钮事件处理        private async void btFindBluetooth_Click(object
sender, RoutedEventArgs e)        {            try
           {                //开始查找对等项                PeerFinder.Start();                    // 等待找到的对等项                var peers = await PeerFinder.FindAllPeersAsync();                if
(peers.Count == 0)                {                    MessageBox.Show("没有发现对等的蓝牙应用");                }                else                {                    // 把对等项目绑定到列表中                    lbBluetoothApp.ItemsSource
= peers;                                }            }            catch(Exception ex)           
{                if ((uint)ex.HResult
== 0x8007048F)                {                    MessageBox.Show("Bluetooth已关闭请打开手机的蓝牙开关");                }                else                {                   
MessageBox.Show(ex.Message);                }            }        }       
// 连接蓝牙对等项的按钮事件处理        private
async void
btConnect_Click(object sender,
RoutedEventArgs e)        {            Button deleteButton = sender
as Button;            PeerInformation selectedPeer =
deleteButton.DataContext as PeerInformation;           
// 连接到选择的对等项            _socket = await
PeerFinder.ConnectAsync(selectedPeer);            //
使用输出输入流建立数据读写对象            _dataReader = new
DataReader(_socket.InputStream);            _dataWriter = new DataWriter(_socket.OutputStream);            //
开始读取消息            PeerFinder_StartReader();        }       
// 读取消息        async void PeerFinder_StartReader()        {      
     try            {                uint
bytesRead = await _dataReader.LoadAsync(sizeof(uint));                if (bytesRead > 0)               
{                    // 获取消息内容的大小                    uint strLength = (uint)_dataReader.ReadUInt32();                    bytesRead = await _dataReader.LoadAsync(strLength);                    if (bytesRead > 0)                   
{                        String
message = _dataReader.ReadString(strLength);                        MessageBox.Show("获取到消息:" + message);                        // 开始下一条消息读取
                       PeerFinder_StartReader();                    }                    else                    {                        MessageBox.Show("对方已关闭连接");                    }                }                else                {                    MessageBox.Show("对方已关闭连接");                }            }            catch (Exception
e)            {                MessageBox.Show("读取失败: " + e.Message);            }        }       
// 页面加载事件处理        void
MainPage_Loaded(object sender,
RoutedEventArgs e)        {            //
订阅连接请求事件            PeerFinder.ConnectionRequested += PeerFinder_ConnectionRequested;        }       
// 连接请求事件处理        void
PeerFinder_ConnectionRequested(object sender, ConnectionRequestedEventArgs args)        {            //
连接并且发送消息            ConnectToPeer(args.PeerInformation);        }       
// 连接并发送消息给对方        async void ConnectToPeer(PeerInformation
peer)        {            _socket = await PeerFinder.ConnectAsync(peer);            _dataReader = new DataReader(_socket.InputStream);            _dataWriter = new DataWriter(_socket.OutputStream);            string
message = "测试消息";            uint strLength = _dataWriter.MeasureString(message);            _dataWriter.WriteUInt32(strLength);//写入消息的长度           
_dataWriter.WriteString(message);//写入消息的内容            uint
numBytesWritten = await _dataWriter.StoreAsync();       
}     }}

程序的运行效果如图19.2所示。

实例:实现蓝牙程序对设备的连接

下面给出蓝牙程序对设备连接的示例:查找蓝牙设备,并对找到的第一个蓝牙设备进行连接。
代码清单19-2:蓝牙程序对设备连接(源代码:第19章/Examples_19_2)

MainPage.xaml文件主要代码

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">             <StackPanel>                <Button x:Name="btFindBluetooth" Content="连接周围的蓝牙设备" Click="btFindBluetooth_Click"/>            </StackPanel>        </Grid>

MainPage.xaml.cs文件主要代码

// 查找蓝牙设备事件处理       
private async
void btFindBluetooth_Click(object sender,
RoutedEventArgs e)        {            try
           {                //
配置PeerFinder蓝牙服务的GUID去搜索设备                PeerFinder.AlternateIdentities["Bluetooth:SDP"] = "5bec6b8f-7eba-4452-bf59-1a510745e99d";                var peers = await PeerFinder.FindAllPeersAsync();                if
(peers.Count == 0)                {                    Debug.WriteLine("没发现蓝牙设备");                }                else                {                    // 连接找到的第一个蓝牙设备                    PeerInformation
selectedPeer = peers[0];                    StreamSocket
socket = new StreamSocket();                    await socket.ConnectAsync(selectedPeer.HostName,
selectedPeer.ServiceName);                   
MessageBox.Show("连接上了HostName:" + selectedPeer.HostName + "ServiceName:"
+ selectedPeer.ServiceName);                }            }            catch
(Exception ex)            {                if
((uint)ex.HResult == 0x8007048F)               
{                   
MessageBox.Show("Bluetooth is
turned off");                }            }        }

基本就是这些了。文章里的一些代码取自一个叫麦子学院IT在线教育平台,这里注明一下。

时间: 2024-10-31 15:39:21

Windows Phone 8 蓝牙编程的相关文章

在windows下的QT编程中的_TCHAR与QString之间的转换

由于在windows下的QT编程中,如果涉及到使用微软的API,那么不可避免使用_TCHAR这些类型,因此在网上查了一下,其中一个老外的论坛有人给出了这个转换,因此在这里做一下笔记 : )#ifdef UNICODE #define QStringToTCHAR(x)     (wchar_t*) x.utf16() #define PQStringToTCHAR(x)    (wchar_t*) x->utf16() #define TCHARToQString(x)     QString:

Windows客户端C/C++编程规范“建议”——函数

1 函数 1.1 代码行数控制在80行及以内 等级:[要求] 说明:每个函数的代码行数控制应该控制在80行以内.如果超过这个限制函数内部逻辑一般可以拆分.如果试图超过这个标准,请列出理由.但理由不包含如下: 无法拆分. 流程内部逻辑复杂,无需拆分,即使拆分了,拆分的函数也不会被其他地方用到.(解释:拆分可以减少代码行数,提炼后的函数可以方便读者快速理解函数逻辑并定位问题.) 1.2 代码列数控制在100字符及以内 等级:[要求] 说明:每行代码不可以超过100字符.如果超过这个字符数,代码的美观

Windows客户端C/C++编程规范“建议”——指针

2 指针 2.1 尽量使用智能指针 等级:[推荐] 说明:正确使用智能指针可以省去指针管理的工作. 2.2 类成员变量指针释放后一定要置空 等级:[必须] 说明:如果类成员变量指针在释放后没有置空,将出现如下问题: a) 无法判断指针是否已经是野指针 b) Dump分析很难发现是野指针函数调用导致崩溃 2.3 正确使用delete和delete[] 等级:[必须] 说明:delete[]用于释放动态分配的数组,而delete用于释放对象.两者不可以混用. 2.4 使用指针前要判空 等级:[必须]

Windows客户端C/C++编程规范“建议”——函数调用

3 函数调用 3.1 谨慎使用递归方法 等级:[推荐] 说明:递归方式控制不当,可能会导致栈空间不够而崩溃.一般的递归都可以使用循环代替. 3.2 不要使用using namespace 等级:[必须] 说明:这是曾经教科书上的一种写法,但是该方法存在严重的缺陷.因为如果多个不同的namespace里定义了相同名字的变量或者函数.将导致无法预知和理解编译器最终使用的是哪个命名空间中的数据. 例子: //file1 namespace Space1{ int g_Private = 0; }; /

Windows客户端C/C++编程规范“建议”——前言

前言 工作中接触了很多编程规范.其中最有意思的是,公司最近发布了一版C/C++编程规范,然后我看到该规范的最后一段时,有这么一句:"该规范不适用于Windows平台开发".看来这份规范是由做其他平台开发的同学制定的.那么做Windows开发的人都去哪儿了?后来由于工作需要,项目组需要我制定一份编程规范.这也是我这系列博客的由来.(转载请指明出于breaksoftware的csdn博客) 说到"规范"",可能没多少人喜欢这样的东西.相信很多工程师和我一样,都

Windows客户端C/C++编程规范“建议”——表达式和运算

4 表达式和运算 4.1 比较操作中将常量设置为左值 等级:[推荐] 说明:编写代码时,如果将常量设置为右值.可能因马虎将"=="写成"="导致逻辑错误.这种场景下,编译器是不会报错的,代码检查也比较容易被忽视. 例子: std::string::size_type index = str.find("a"); if ( index = std::string::npos){ } 上例中写法可以执行,但是逻辑是错的.如下编写,可以借助编译器检查出

Windows客户端C/C++编程规范“建议”——结构

5 结构 5.1 不要使用goto 等级:[必须] 说明:在大型项目中,goto的滥用会导致灾难性后果.因为我们程序中一般不存在从一个函数体内部跳转到另一个函数体内部的场景,所以我们可以将跳转控制在函数内部,从而避免灾难. 例子: do { if ( False ) { break;// 相当于goto } } while (0); 5.2 不要利用异常机制实现流程的跳转 等级:[必须] 说明:该方法比较常见于防逆向等方面,但是我们普通编程方式应该严禁使用.否则将增加代码阅读的难度. (转载请指

Windows客户端C/C++编程规范“建议”——宏

6 宏 6.1 减少宏的使用 等级:[建议] 说明:宏的使用,将使得调试变得麻烦.所以在设计和使用宏的时候,请确保宏的逻辑是阅读者不会去关心细节的行为. 6.2 宏定义中字母需大写 等级:[必须] 说明:为了醒目表示它是一个宏,而不是一个函数. 6.3 使用const变量代替宏定义值 等级:[建议] 说明:在一个函数体内部使用的常量,最好使用const变量替代,而不是使用宏. 6.4 使用枚举代替一系列有关联的宏 等级:[建议] 说明:比如一个函数返回一系列表示状态的宏,则应该使用枚举类型替代.

Windows客户端C/C++编程规范“建议”——文件

7 文件 7.1 正确使用#include 等级:[推荐] 说明:#include <>和#include ""导致编译器在搜索文件时,搜索的路径顺序不同.所以需要正确使用#include,以避免包含错了头文件. 语法形式 操作 带引号的形式 预处理器按以下顺序搜索包含文件: 在包含 #include 语句的文件所在的同一目录中. 在当前打开的包含文件的目录中,采用与打开它们的顺序相反的顺序. 搜索从父包含文件的目录中开始进行,然后继续向上到任何祖父包含文件的目录. 跟随每