技术开发 频道

.NET Compact Framework下的Bluetooth广播程序的开发

 【IT168技术文档】

 简述

 本文讲述如何使用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线程挂死。

0
相关文章