藍牙是手機的近距離無限傳輸?shù)募夹g(shù),在之前的Windows Phone 7系統(tǒng)手機里面僅支持藍牙耳機功能,并不支持藍牙文件信息傳輸,那么在Windows Phone 8手機里面將全面支持藍牙技術(shù),并且提供了相關(guān)的API來給開發(fā)者使用。開發(fā)者可以利用藍牙的相關(guān)API來創(chuàng)建應(yīng)用程序,在應(yīng)用程序里面使用手機的藍牙技術(shù)來進行近距離的文件傳輸和發(fā)送接收消息,創(chuàng)造出更加有趣和方便的應(yīng)用軟件。
在Windows Phone 8里面可以在應(yīng)用程序里面利用藍牙進行通信,使用藍牙相關(guān)的API,可以讓應(yīng)用程序連接到另外的一個應(yīng)用程序,也可以讓應(yīng)用程序連接到一個設(shè)備上。Windows Phone 8的藍牙技術(shù)支持兩個藍牙方案:一個是應(yīng)用程序到應(yīng)用程序的通信,另外一個是應(yīng)用程序到設(shè)備的通信。
1.應(yīng)用程序到應(yīng)用程序的通信
應(yīng)用程序到應(yīng)用程序的通信的過程是,應(yīng)用程序使用藍牙去查找正在廣播藍牙服務(wù)的對等的應(yīng)用程序,如果在應(yīng)用程序提供服務(wù)的范圍內(nèi)發(fā)現(xiàn)一個應(yīng)用程序,那么該應(yīng)用程序可以發(fā)起連接請求。當(dāng)這兩個應(yīng)用程序接受連接,它們之間就可以進行通信了,通信的過程是使用socket的消息發(fā)送接收機制。在Windows Phone 8中使用到應(yīng)用程序到應(yīng)用程序的藍牙通訊技術(shù),需要在項目的WMAppManifest.xml文件中添加ID_CAP_PROXIMITY的功能選項,表示支持臨近的設(shè)備通信能力,否則程序會出現(xiàn)異常。
2.應(yīng)用程序到設(shè)備的通信
在應(yīng)用程序到設(shè)備的通信過程時,應(yīng)用程序使用藍牙去查找提供服務(wù)的設(shè)備,如果提供的服務(wù)范圍之內(nèi)發(fā)現(xiàn)一個可以連接的藍牙設(shè)備,那么該應(yīng)用程序可以發(fā)起連接請求。當(dāng)應(yīng)用程序和設(shè)備同時接受該連接,它們之間就可以進行通信了,通信的過程也是使用socket的消息發(fā)送接收機制,類似于應(yīng)用程序到應(yīng)用程序的通信。在Windows Phone 8中使用到應(yīng)用程序到設(shè)備的藍牙通訊技術(shù),需要在項目的WMAppManifest.xml文件中添加ID_CAP_PROXIMITY和ID_CAP_NETWORKING的功能選項,表示支持臨近的設(shè)備通信能力和網(wǎng)絡(luò)通信能力,否則程序會出現(xiàn)異常。
藍牙編程類
在Windows Phone 8里面使用到藍牙編程主要會用到PeerFinder類,PeerInformation類,StreamSocket類和ConnectionRequestedEventArgs類,這些類的說明如表19.1所示。因為藍牙也是基于TCP協(xié)議進行消息傳遞了,所以需要用到Socket的相關(guān)的編程知識,以及StreamSocket類。PeerFinder類是藍牙查找類,它的主要成員如表19.2所示。
表19.1 藍牙編程類的說明
類名 | 說明 |
PeerFinder | 用于去查找附近的設(shè)備是否有運行和當(dāng)前應(yīng)用程序相同的應(yīng)用程序,并且可以在兩個應(yīng)用程序之間建立起socket連接,從而可以進行通信。對等應(yīng)用程序是在其他設(shè)備上運行的應(yīng)用程序的另一個實例。 |
PeerInformation | 包含對等應(yīng)用程序或設(shè)備的識別信息。 |
StreamSocket | 支持使用一個TCP的Socket流的網(wǎng)絡(luò)通信。 |
ConnectionRequestedEventArgs | 表示傳遞到一個應(yīng)用程序的ConnectionRequested事件的屬性 |
表 19.2 PeerFinder類的成員
成員 | 說明 |
bool AllowBluetooth | 指定 PeerFinder 類的此實例是否可以通過使用 Bluetooth 來連接 ProximityStreamSocket 對象。如果PeerFinder 的此實例可以通過使用 Bluetooth 來連接 ProximityStreamSocket 對象,則為 true;否則為false。默認為 true。 |
bool AllowInfrastructure | 是否使用TCP/IP協(xié)議連接到StreamSocket |
bool AllowWiFiDirect | 指定 PeerFinder 類的此實例是否可以通過使用 Wi-Fi Direct 來連接 ProximityStreamSocket 對象。如果 PeerFinder 的此實例可以通過使用 Wi-Fi Direct 來連接 ProximityStreamSocket 對象,則為 true;否則為false。默認為 true。 |
IDictionary<string, string> AlternateIdentities | 獲取要與其他平臺上的對等應(yīng)用程序匹配的備用 AppId 值列表。返回要與其他平臺的對等類應(yīng)用程序匹配的備用 AppId 值列表。 |
string DisplayName | 獲取或設(shè)置標識計算機到遠程對等類的名稱。 |
PeerDiscoveryTypes SupportedDiscoveryTypes | 獲取一個值,該值指示哪些發(fā)現(xiàn)選項可與 PeerFinder 類一同使用 |
event TypedEventHandler<object, ConnectionRequestedEventArgs> ConnectionRequested | 遠程對等類使用 ConnectAsync 方法請求連接時發(fā)生。 |
event TypedEventHandler<object, TriggeredConnectionStateChangedEventArgs> TriggeredConnectionStateChanged | 在遠程對等類的輕擊筆勢期間發(fā)生。 |
IAsyncOperation< StreamSocket> ConnectAsync(PeerInformation peerInformation) | 連接已發(fā)現(xiàn)了對 FindAllPeersAsync 方法的調(diào)用的對等類。peerInformation:表示連接到的對等類的對等類信息對象。返回通過使用所提供的臨近StreamSocket 對象連接遠程對等類的異步操作。 |
IAsyncOperation<IReadOnlyList<PeerInformation>> FindAllPeersAsync() | 適用于無線范圍內(nèi)運行相同應(yīng)用程序的對等計算機的異步瀏覽。返回通過使用 Wi-Fi直連技術(shù)瀏覽對等類的異步操作。 |
void Start(string peerMessage) | 向臨近設(shè)備上的對等類應(yīng)用程序傳遞消息。 |
void Stop() | 停止查找對等類應(yīng)用程序或廣播對等類連接的過程 |
查找藍牙設(shè)備和對等項
查找在服務(wù)范圍內(nèi)的藍牙設(shè)備和對等項是藍牙編程的第一步,查找藍牙設(shè)備和對等項中會使用到PeerFinder類的FindAllPeersAsync方法去進行查找,然后以異步的方式返回查找到的對等項列表的信息IReadOnlyList<PeerInformation>,注意要使查找對等的應(yīng)用程序時,在調(diào)用FindAllPeersAsync方法前必須先調(diào)用PeerFinder類的Start方法,主要的目的是啟動廣播服務(wù),讓對方的應(yīng)用程序也能查找到自己。PeerInformation包含三個屬性:一個是DisplayName表示對等項的名字,這個名字一般都是由對方的設(shè)備的名稱或者查找到的應(yīng)用程序自身設(shè)置的現(xiàn)實名字,一個是HostName表示主機名字或者IP地址,還有一個屬性是ServiceName表示服務(wù)名稱或者TCP協(xié)議的端口號。然后可以利用查找到的PeerInformation信息進行連接和通信。
查找對等的應(yīng)用程序的代碼示例:
async void AppToApp() { // 啟動查找服務(wù) PeerFinder.Start(); //開始查找 ObservableCollection<PeerInformation> peers = await PeerFinder.FindAllPeersAsync(); if (peers.Count == 0) { //未找到任何的對等項 } else { //處理查找到的對等項,可以使用PeerFinder類的ConnectAsync方法來連接選擇的要進行通信的對等項 } }
查找藍牙設(shè)備的代碼示例:
private async void AppToDevice() { // 設(shè)置查找所匹配的藍牙設(shè)備 PeerFinder.AlternateIdentities["Bluetooth:Paired"] = ""; // 開始查找 ObservableCollection<PeerInformation> pairedDevices = await PeerFinder.FindAllPeersAsync(); if (pairedDevices.Count == 0) { // 沒有找到可用的藍牙設(shè)備 } else { //處理查找到的藍牙設(shè)備,可以新建一個StreamSocket對象,然后使用StreamSocket類的ConnectAsync方法通過HostName和ServiceName來連接藍牙設(shè)備 } }
藍牙發(fā)送消息
藍牙編程的發(fā)送消息機制使用的是TCP的StreamSocket的方式,原理與Socket的一致。在藍牙連接成功后,可以獲取到一個StreamSocket類的對象,然后我們使用該對象的OutputStream屬性來初始化一個DataWriter對象,通過DataWriter對象來進行發(fā)送消息。OutputStream屬性表示的是Socket的輸出流,用于發(fā)送消息給對方。下面來看一下發(fā)送消息的示例:
async void SendMessage(string message) { // 連接選中的對等項,selectedPeer為查找到的PeerInformation對象 StreamSocket _socket= = await PeerFinder.ConnectAsync(selectedPeer); // 創(chuàng)建DataWriter DataWriter _dataWriter = new DataWriter(_socket.OutputStream); // 先寫入發(fā)送消息的長度 _dataWriter.WriteInt32(message.Length); await _dataWriter.StoreAsync(); // 最后寫入發(fā)送消息的內(nèi)容 _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); // 創(chuàng)建DataReader DataReader _dataReader = new DataReader(_socket.InputStream); // 先讀取消息的長度 await _dataReader.LoadAsync(4); uint messageLen = (uint)_dataReader.ReadInt32(); // 最后讀取消息的內(nèi)容 await _dataReader.LoadAsync(messageLen); return _dataReader.ReadString(messageLen); }
實例:實現(xiàn)藍牙程序?qū)Τ绦虻膫鬏?/strong>
下面給出藍牙程序?qū)Τ绦騻鬏數(shù)氖纠和ㄟ^使用藍牙功能查找周邊也要使用改應(yīng)用的手機,互相建立起連接和發(fā)送測試消息。
代碼清單19-1:藍牙程序?qū)Τ绦騻鬏敚ㄔ创a:第19章\Examples_19_1)
MainPage.xaml文件主要代碼
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <StackPanel> <Button x:Name="btFindBluetooth" Content="通過藍牙查找該應(yīng)用設(shè)備" 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數(shù)據(jù)流對象
private DataWriter _dataWriter; // 數(shù)據(jù)寫入對象
private DataReader _dataReader; // 數(shù)據(jù)讀取對象
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("沒有發(fā)現(xiàn)對等的藍牙應(yīng)用");
}
else
{
// 把對等項目綁定到列表中
lbBluetoothApp.ItemsSource = peers;
}
}
catch(Exception ex)
{
if ((uint)ex.HResult == 0x8007048F)
{
MessageBox.Show("Bluetooth已關(guān)閉請打開手機的藍牙開關(guān)");
}
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);
// 使用輸出輸入流建立數(shù)據(jù)讀寫對象
_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)
{
// 獲取消息內(nèi)容的大小
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("對方已關(guān)閉連接");
}
}
else
{
MessageBox.Show("對方已關(guān)閉連接");
}
}
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)
{
// 連接并且發(fā)送消息
ConnectToPeer(args.PeerInformation);
}
// 連接并發(fā)送消息給對方
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);//寫入消息的內(nèi)容
uint numBytesWritten = await _dataWriter.StoreAsync();
}
}
}
程序的運行效果如圖19.2所示。
實例:實現(xiàn)藍牙程序?qū)υO(shè)備的連接
下面給出藍牙程序?qū)υO(shè)備連接的示例:查找藍牙設(shè)備,并對找到的第一個藍牙設(shè)備進行連接。
代碼清單19-2:藍牙程序?qū)υO(shè)備連接(源代碼:第19章\Examples_19_2)
MainPage.xaml文件主要代碼
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel>
<Button x:Name="btFindBluetooth" Content="連接周圍的藍牙設(shè)備" Click="btFindBluetooth_Click"/>
</StackPanel>
</Grid>
MainPage.xaml.cs文件主要代碼
// 查找藍牙設(shè)備事件處理
private async void btFindBluetooth_Click(object sender, RoutedEventArgs e)
{
try
{
// 配置PeerFinder藍牙服務(wù)的GUID去搜索設(shè)備
PeerFinder.AlternateIdentities["Bluetooth:SDP"] = "5bec6b8f-7eba-4452-bf59-1a510745e99d";
var peers = await PeerFinder.FindAllPeersAsync();
if (peers.Count == 0)
{
Debug.WriteLine("沒發(fā)現(xiàn)藍牙設(shè)備");
}
else
{
// 連接找到的第一個藍牙設(shè)備
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");
}
}
}
程序的運行效果如圖19.3所示