蓝牙通信,完整的通信流程!

无线通信方案,有三种方案可以实施:
1、NFC 2、蓝牙 3、WIFI
下面是对这三个知识点做的一个总结,参照对比可以选择合适的方案。而本章着重讲的蓝牙之间通信。
蓝牙通信,完整的通信流程!_第1张图片

首先介绍一下蓝牙的两个广播Receiver。
第一个:蓝牙状态的改变是通过广播接收到的。

 // 注册蓝牙状态接收广播
  IntentFilter intentFilter = new  ntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
   registerReceiver(mReceiverBluetoothStatus,intentFilter);

    /**
     * 定义接收蓝牙状态广播receiver
     *
     * @param savedInstanceState
     */
    private BroadcastReceiver mReceiverBluetoothStatus = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context,Intent intent) {
            int status = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,-1);
            switch (status) {
                case BluetoothAdapter.STATE_OFF:
                    Log.d(TAG,"蓝牙已关闭");
                    break;
                case BluetoothAdapter.STATE_ON:
                    Log.d(TAG,"蓝牙已打开");
                    break;
                case BluetoothAdapter.STATE_TURNING_OFF:
                    Log.d(TAG,"蓝牙关闭中...");
                    break;
                case BluetoothAdapter.STATE_TURNING_ON:
                    Log.d(TAG,"蓝牙打开中...");
                    break;
                default:

                    break;
            }
        }
    };

第二个:蓝牙搜索到设备、绑定设备(配对)也是通过广播接收的。(搜索到设备系统会自动发一个广播)

   // 注册蓝牙device接收广播
        IntentFilter intentFilterDevice = new IntentFilter();
        // 开始查找
        intentFilterDevice.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        // 结束查找
        intentFilterDevice.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        // 查找设备(查找到设备)
        intentFilterDevice.addAction(BluetoothDevice.ACTION_FOUND);
        // 设备扫描模式改变 (自己状态的改变action,当设置可见或者不见时都会发送此广播)
        intentFilterDevice.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
        // 绑定状态
        intentFilterDevice.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        registerReceiver(mReceiverDeceiver,intentFilterDevice);
  /**
     * 定义接收蓝牙device广播Receiver
     */
    private BroadcastReceiver mReceiverDeceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context,Intent intent) {
            String action = intent.getAction();
            if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
                // 开始搜索        ——接收广播
                Log.d(TAG,"开始搜索");
                mList.clear();
                mAdapter.refresh(mList);

            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
                // 查找到设备完成   —— 接收广播
                Log.d(TAG,"查找到设备完成");

            } else if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                // 搜索到设备       —— 接收广播
                Log.d(TAG,"搜索到设备");
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                mList.add(device);
                mAdapter.refresh(mList);


            } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
                // 当自己设备设置蓝牙可见时或者不可见时 —— 接收广播
                int scanMode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,0);
                // 可见时
                if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
                    Log.d(TAG,"设备可见监听");
                } else {
                    Log.d(TAG,"设备不可见监听");
                }

            } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
                // 绑定状态改变回调
                BluetoothDevice remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if (remoteDevice == null) {
                    Log.d(TAG,"没有绑定设备");
                    return;
                }

                int status = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,0);
                if (status == BluetoothDevice.BOND_BONDED) {
                    Log.d(TAG,"绑定设备完成: " + remoteDevice.getName());
                } else if (status == BluetoothDevice.BOND_BONDING) {
                    Log.d(TAG,"绑定设备中: " + remoteDevice.getName());
                } else if (status == BluetoothDevice.BOND_NONE) {
                    Log.d(TAG,"取消绑定: ");
                }
            }
        }

    };

以上基本上就是蓝牙接收状态和搜索到设备,绑定设备一系列改变的操作!基本上已经很全了。下面介绍一个蓝牙通信的流程。

左为客户端Socket连接的一个流程:首先获取一个客户端Socket(),然后连接上,就可以读取数据和发送数据了。

右为服务端Socket操作流程:

蓝牙通信原理介绍:
蓝牙通信和socket通信原理基本上是一致的,下面我给大家上一张图(图为Socket通信图)。分析一下。
蓝牙通信,完整的通信流程!_第2张图片

蓝牙客户端Socket的与Sokcet流程是一样的,只不过参数不同而已。如下:
1、创建客户端蓝牙Sokcet
2、创建连接
3、读写数据
4、关闭

服务端socket:
1、创建服务端蓝牙Socket
2、绑定端口号(蓝牙忽略)
3、创建监听listen(蓝牙忽略, 蓝牙没有此监听,而是通过whlie(true)死循环来一直监听的)
4、通过accept(),如果有客户端连接,会创建一个新的Socket,体现出并发性,可以同时与多个socket通讯)
5、读写数据
6、关闭

下面看客户端代码:

/**
 * 

Title: ConnectThread

*

Description: 客户端逻辑: 客户端的线程,处理客户端socket

*

Company: ihaveu

* * @author MaWei * @date 2017/12/26 */
public class ConnectThread extends Thread{ private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID); /** 客户端socket*/ private final BluetoothSocket mmSoket; /** 要连接的设备*/ private final BluetoothDevice mmDevice; private BluetoothAdapter mBluetoothAdapter; /** 主线程通信的Handler*/ private final Handler mHandler; /** 发送和接收数据的处理类*/ private ConnectedThread mConnectedThread; public ConnectThread(BluetoothDevice device, BluetoothAdapter bluetoothAdapter, Handler mUIhandler) { mmDevice = device; mBluetoothAdapter = bluetoothAdapter; mHandler = mUIhandler; BluetoothSocket tmp = null; try { // 创建客户端Socket tmp = device.createRfcommSocketToServiceRecord(MY_UUID); } catch (IOException e) { e.printStackTrace(); } mmSoket = tmp; } @Override public void run() { super.run(); // 关闭正在发现设备.(如果此时又在查找设备,又在发送数据,会有冲突,影响传输效率) mBluetoothAdapter.cancelDiscovery(); try { // 连接服务器 mmSoket.connect(); } catch (IOException e) { // 连接异常就关闭 try { mmSoket.close(); } catch (IOException e1) { } return; } manageConnectedSocket(mmSoket); } private void manageConnectedSocket(BluetoothSocket mmSoket) { // 通知主线程连接上了服务端socket,更新UI mHandler.sendEmptyMessage(Constant.MSG_CONNECTED_TO_SERVER); // 新建一个线程进行通讯,不然会发现线程堵塞 mConnectedThread = new ConnectedThread(mmSoket,mHandler); mConnectedThread.start(); } /** * 关闭当前客户端 */ public void cancle() { try { mmSoket.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 发送数据 * @param data */ public void sendData(byte[] data) { if(mConnectedThread != null) { mConnectedThread.write(data); } } }

服务端代码:

/**
 * 

Title: AccepThread

*

Description: 服务端Socket通过accept()一直监听客户端连接的线程

*

Company: ihaveu

* * @author MaWei * @date 2017/12/26 */
public class AccepThread extends Thread { /** 连接的名称*/ private static final String NAME = "BluetoothClass"; /** UUID*/ private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID); /** 服务端蓝牙Sokcet*/ private final BluetoothServerSocket mmServerSocket; private final BluetoothAdapter mBluetoothAdapter; /** 线程中通信的更新UI的Handler*/ private final Handler mHandler; /** 监听到有客户端连接,新建一个线程单独处理,不然在此线程中会堵塞*/ private ConnectedThread mConnectedThread; public AccepThread(BluetoothAdapter adapter, Handler handler) throws IOException { mBluetoothAdapter = adapter; this.mHandler = handler; // 获取服务端蓝牙socket mmServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID); } @Override public void run() { super.run(); // 连接的客户端soacket BluetoothSocket socket = null; // 服务端是不退出的,要一直监听连接进来的客户端,所以是死循环 while (true){ // 通知主线程更新UI,客户端开始监听 mHandler.sendEmptyMessage(Constant.MSG_START_LISTENING); try { // 获取连接的客户端socket socket = mmServerSocket.accept(); } catch (IOException e) { // 通知主线程更新UI, 获取异常 mHandler.sendEmptyMessage(Constant.MSG_ERROR); e.printStackTrace(); // 服务端退出一直监听线程 break; } if(socket != null) { // 管理连接的客户端socket manageConnectSocket(socket); // 这里应该是手动断开,案例应该是只保证连接一个客户端,所以连接完以后,关闭了服务端socket // try { // mmServerSocket.close(); // mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING); // } catch (IOException e) { // e.printStackTrace(); // } } } } /** * 管理连接的客户端socket * @param socket */ private void manageConnectSocket(BluetoothSocket socket) { // 只支持同时处理一个连接 // mConnectedThread不为空,踢掉之前的客户端 if(mConnectedThread != null) { mConnectedThread.cancle(); } // 主线程更新UI,连接到了一个客户端 mHandler.sendEmptyMessage(Constant.MSG_GOT_A_CLINET); // 新建一个线程,处理客户端发来的数据 mConnectedThread = new ConnectedThread(socket, mHandler); mConnectedThread.start(); } /** * 断开服务端,结束监听 */ public void cancle() { try { mmServerSocket.close(); mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING); } catch (IOException e) { e.printStackTrace(); } } /** * 发送数据 * @param data */ public void sendData(byte[] data){ if(mConnectedThread != null) { mConnectedThread.write(data); } } }

下面看一个共同通讯处理类:

/**
 * 

Title: ConnectedThread

*

Description: 客户端和服务端 处理 发送数据 和获取数据

*

Company: ihaveu

* * @author MaWei * @date 2017/12/26 */
public class ConnectedThread extends Thread{ /** 当前连接的客户端BluetoothSocket*/ private final BluetoothSocket mmSokcet; /** 读取数据流*/ private final InputStream mmInputStream; /** 发送数据流*/ private final OutputStream mmOutputStream; /** 与主线程通信Handler*/ private Handler mHandler; private String TAG = "ConnectedThread"; public ConnectedThread(BluetoothSocket socket,Handler handler) { mmSokcet = socket; mHandler = handler; InputStream tmpIn = null; OutputStream tmpOut = null; try { tmpIn = socket.getInputStream(); tmpOut = socket.getOutputStream(); } catch (IOException e) { e.printStackTrace(); } mmInputStream = tmpIn; mmOutputStream = tmpOut; } @Override public void run() { super.run(); byte[] buffer = new byte[1024]; while (true) { try { // 读取数据 int bytes = mmInputStream.read(buffer); if(bytes > 0) { String data = new String(buffer,0,bytes,"utf-8"); // 把数据发送到主线程, 此处还可以用广播 Message message = mHandler.obtainMessage(Constant.MSG_GOT_DATA,data); mHandler.sendMessage(message); } Log.d(TAG, "messge size :" + bytes); } catch (IOException e) { e.printStackTrace(); } } } // 踢掉当前客户端 public void cancle() { try { mmSokcet.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 服务端发送数据 * @param data */ public void write(byte[] data) { try { mmOutputStream.write(data); } catch (IOException e) { e.printStackTrace(); } } }

下面是自己写的一个聊天demo

/**
 * 

Title: ChatController

*

Description: 聊天控制器

*

Company: ihaveu

* * @author MaWei * @date 2017/12/26 */
public class ChatController { /** 客户端的线程*/ private ConnectThread mConnectThread; /** 服务端的线程*/ private AccepThread mAccepThread; private ChatProtocol mProtocol = new ChatProtocol(); /** * 网络协议的处理函数 */ private class ChatProtocol implements ProtocoHandler<String>{ private static final String CHARSET_NAME = "utf-8"; /** * 封包(发送数据) * 把发送的数据变成 数组 2进制流 */ @Override public byte[] encodePackge(String data) { if(data == null) { return new byte[0]; }else { try { return data.getBytes(CHARSET_NAME); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return new byte[0]; } } } /** * 解包(接收处理数据) * 把网络上数据变成自己想要的数据体 */ @Override public String decodePackage(byte[] netData) { if(netData == null) { return ""; }else { try { return new String(netData, CHARSET_NAME); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return ""; } } } } /** * 与服务器连接进行聊天 */ public void startChatWith(BluetoothDevice device,BluetoothAdapter adapter,Handler handler){ mConnectThread = new ConnectThread(device, adapter, handler); mConnectThread.start(); } /** * 等待客户端来连接 * handler : 用来跟主线程通信,更新UI用的 */ public void waitingForFriends(BluetoothAdapter adapter, Handler handler) { try { mAccepThread = new AccepThread(adapter,handler); mAccepThread.start(); } catch (IOException e) { e.printStackTrace(); } } /** * 发出消息 */ public void sendMessage(String msg){ // 封包 byte[] data = mProtocol.encodePackge(msg); if(mConnectThread != null) { mConnectThread.sendData(data); }else if(mAccepThread != null) { mAccepThread.sendData(data); } } /** * 网络数据解码 */ public String decodeMessage(byte[] data){ return mProtocol.decodePackage(data); } /** * 停止聊天 */ public void stopChart(){ if(mConnectThread != null) { mConnectThread.cancle(); } if(mAccepThread != null) { mAccepThread.cancle(); } } /** * 以下是单例写法 */ private static class ChatControlHolder{ private static ChatController mInstance = new ChatController(); } public static ChatController getInstance(){ return ChatControlHolder.mInstance; } }

OK,完成,大家主要看上面的广播,和蓝牙sokcet流程,其他的根据自己业务要求写就OK

你可能感兴趣的:(技术分析)