文章目錄
- 成員定義
- 啟動服務
- 監聽線程
- 廣播線程
- 關閉服務
- UI處理
- 發現
- 訂閱
- 接收訊息
簡述
本文講述如何使用32feet.NET實現Bluetooth的廣播程式,同時示範了Broadcom stack在Windows Mobilie下的實現。
背景
在.NET Compact Framework下的Bluetooth開發 之 32feet.NET 的反饋中 camper9999 同學希望實現藍芽廣播的功能,本文就是一個基於32feet.NET藍芽廣播的實現。
sammylp 的提出代碼掛死問題,其實是使用過程的不恰當造成的,本文示範如何使用線程防止UI線程的掛起,程式的假死。
另外一個同學(不好意思忘記哪位了)問32feet.NET是否支援Broadcom stack,所以本文的實現運行於安裝Broadcom stack的windows mobile中。
感謝各位的反饋,現在盡量在一篇文章中回答。
關於Bluetooth開發的也可以參考以下其他文章:
.NET Compact Framework下的Bluetooth開發 之 Windows Embedded Source Tools for Bluetooth
.NET Compact Framework下的Bluetooth開發 之 32feet.NET
.NET Compact Framework下的Bluetooth開發 之 Bluetooth Virtual Serial Port
.NET Compact Framework下的Bluetooth裝置的配對
30 Days of .NET [Windows Mobile Applications] - Day 02: Bluetooth Manager(藍芽管理器)
什麼是廣播
所謂廣播就是訊息發送方向公眾(public)發送資訊的過程,廣播有一個主要的特點是訊息發送方不需要知道訊息接收方的存在。現實生活中廣播的例子如收音機廣播,GPS衛星廣播,乙太網路同網段資料包的廣播等等。可是所謂藍芽廣播其實不算嚴格下的廣播,因為藍芽通訊過程中有發現,配對,甚至驗證過程,所以通訊雙方是需要握手的,沒辦法實現嚴格意義上的廣播。本文例子實現了一個通過註冊訂閱者式的組播過程(MultiCast)。
實現服務端
服務端負責監聽和註冊服務,同時把訊息發送到已經註冊的裝置去。在例子中服務端使用PC實現,其實可以使用Windows Mobilie作為服務端,32feet.net庫基本相容PC和CE。
成員定義
private BluetoothListener listener;
private bool listening = true;
private List<BluetoothClient> clientList = new List<BluetoothClient>();
private System.Threading.Thread listenThread;
private System.Threading.Thread broadcastThread;
listener負責監聽服務,clientList 存放已經註冊的裝置,listenThread負責監聽的線程,broadcastThread負責廣播的線程。
啟動服務
BluetoothRadio radio = BluetoothRadio.PrimaryRadio;
if (radio == null)
{
WriteMessage("No radio hardware or unsupported software stack");
return;
}
// Enable discoverable mode
radio.Mode = RadioMode.Discoverable;
WriteMessage("Radio Name:" + radio.Name);
WriteMessage("Radio Address:" + radio.LocalAddress);
WriteMessage("Radio Mode now: " + radio.Mode.ToString());
listener = new BluetoothListener(BluetoothService.SerialPort);
listener.Start();
listening = true;
listenThread = new System.Threading.Thread(ListenLoop);
broadcastThread = new System.Threading.Thread(BroadcastLoop);
listenThread.Start();
broadcastThread.Start();
WriteMessage("Service started!");
啟動服務的流程是:
1.檢查藍牙裝置是否準備好。
2.設定藍牙裝置為可發現。
3.啟動藍芽監聽,這裡配置的服務類型為串口服務,在用戶端也需要配置串口服務類型才能進行通訊。
4.啟動監聽線程,這樣不會掛死主線程(Main Thread)。
5.啟動廣播線程。
監聽線程
private void ListenLoop()
{
byte[] buffer = new byte[4];
string dataToSend = "Thanks for subscription";
byte[] dataBuffer = System.Text.ASCIIEncoding.ASCII.GetBytes(dataToSend);
while (listening)
{
try
{
BluetoothClient client = listener.AcceptBluetoothClient();
WriteMessage("Get a subscription from " + client.RemoteMachineName);
clientList.Add(client);
System.IO.Stream ns = client.GetStream();
ns.Write(dataBuffer, 0, dataBuffer.Length);
}
catch
{
break;
}
}
listener.Stop();
}
監聽線程負責處理監聽訂閱請求,並把訂閱的裝置增加到訂閱列表中。AcceptBluetoothClient()會掛起改線程,直到有新的裝置進行訂閱。
廣播線程
private void BroadcastLoop()
{
List<BluetoothClient> tempClientList = new List<BluetoothClient>();
while (listening)
{
System.Threading.Thread.Sleep(5000);
string dataToSend = "Broadcast Message at " + System.DateTime.Now.ToLongTimeString();
byte[] dataBuffer = System.Text.ASCIIEncoding.ASCII.GetBytes(dataToSend);
tempClientList.Clear();
foreach (BluetoothClient client in clientList)
{
try
{
System.IO.Stream ns = client.GetStream();
ns.Write(dataBuffer, 0, dataBuffer.Length);
WriteMessage("Sent message to " + client.RemoteMachineName);
}
catch
{
//connection is broken.
tempClientList.Add(client);
continue;
}
}
//clean up the broken connections.
foreach (BluetoothClient client in tempClientList)
{
clientList.Remove(client);
}
}
}
廣播線程負責對已經訂閱的裝置進行訊息廣播,同時管理已經中斷的超連結。在實際應用中,這個線程需要根據需求來更改商務程序。
關閉服務
WriteMessage("Service stop!");
listening = false;
if (listener != null)
{
listener.Stop();
}
釋放監聽資源。
UI處理
由於使用了多線程,不能直接更新UI,所以需要藉助delegate和Invoke()函數來更新。
public delegate void SafeWinFormsThreadDelegate(string msg);
private void WriteMessage(string msg)
{
SafeWinFormsThreadDelegate d = new SafeWinFormsThreadDelegate(UpdateUi);
Invoke(d, new object[] { msg });
}
private void UpdateUi(string msg)
{
if (listBoxMsg.Items.Count > 100)
{
listBoxMsg.Items.RemoveAt(0);
}
listBoxMsg.SelectedIndex = listBoxMsg.Items.Add(msg);
}
用戶端
用戶端負責探索服務端裝置,同時發起訂閱請求,然後接收廣播訊息。用戶端使用安裝了BroadCom stack的Windows Mobile實現,實際上同時支援MS stack。
發現
BluetoothRadio radio = BluetoothRadio.PrimaryRadio;
if (radio == null)
{
WriteMessage("No radio hardware or unsupported software stack");
return;
}
//Broadcom stack doesn't support the functionality to turn on the bluetooth, turn it on manually please
//radio.Mode = RadioMode.Connectable;
//Scan the nearby devices
listBoxDevices.Items.Clear();
BluetoothDeviceInfo[] devices = client.DiscoverDevices();
listBoxDevices.DataSource = devices;
listBoxDevices.DisplayMember = "DeviceName";
listBoxDevices.ValueMember = "DeviceAddress";
WriteMessage("Discover successful, please select one device to subscribe.");
由於目前的版本的32feet.net在BroadCom stack下不支援設定藍芽狀態,所以如果裝置是BroadCom stack需要屏蔽設定藍芽狀態的語句。把發現到的裝置顯示到ListBox裡面。
訂閱
BluetoothAddress deviceAddress = listBoxDevices.SelectedValue as BluetoothAddress;
client.Connect(deviceAddress, BluetoothService.SerialPort);
WriteMessage("Connected to " + client.RemoteMachineName);
stream = client.GetStream();
receiving = true;
System.Threading.Thread t = new System.Threading.Thread(ReceiveLoop);
t.Start();
根據發現的服務端裝置的地址進行串連,然後啟動線程接收訊息。
接收訊息
private void ReceiveLoop()
{
byte[] buffer = new byte[255];
while (receiving)
{
if (stream.CanRead)
{
stream.Read(buffer, 0, 255);
string data = System.Text.ASCIIEncoding.ASCII.GetString(buffer, 0, 255);
WriteMessage(data);
}
}
}
線上程裡接收訊息,避免主UI線程掛死。
原始碼:http://files.cnblogs.com/procoder/BtBroadcast.rar
平台:Visual Studio 2008 + Windows Mobile 5 Packet PC SDK