精华内容
下载资源
问答
  • android 蓝牙数据传输

    万次阅读 2018-07-30 16:13:57
    因为项目需要,需要将本地数据库的数据,通过蓝牙传输到另一台设备上。然后百度了蛮久,看了蛮多的,觉得有必要自己整理一下,蓝牙传输功能。 首先,我们都知道的,蓝牙连接数需要先配对的,两台手机配对后才可...

     

    因为项目需要,需要将本地数据库的数据,通过蓝牙传输到另一台设备上。然后百度了蛮久,看了蛮多的,觉得有必要自己整理一下,蓝牙的传输功能。

    首先,我们都知道的,蓝牙连接数需要先配对的,两台手机配对后才可进行蓝牙处理。对于蓝牙配对的方法网上一大堆,我也就不具体说了,大概记录一下。基本的我们可以通过手机原有的蓝牙功能,进行搜索配对后在自身的APK中,直接启动蓝牙,然后查询已匹配设备就行了。因为项目目前没有要求实现匹配功能,我也就能省则省了...不过还是总体介绍一下吧。

    在安卓源码中,首先要知道有提供了一个蓝牙类:BluetoothAdapter , 我们需要先初始化这个类,才能huod获得蓝牙的相关数据。 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();  接着我们需要先注册广播,广播的注册有静态注册和动态注册,我就不详说了,我这用的是动态注册: 

      //注册蓝牙广播
            IntentFilter filter = new IntentFilter(
                    BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
            getActivity().registerReceiver(receiver, filter);
            filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
            getActivity().registerReceiver(receiver, filter);
    

    然后再实现广播的处理:

    public class BluetoothReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(BluetoothDevice.ACTION_FOUND)) {
                //未匹配
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
                //搜索完成
            }
        }
    
    }

    基本就是这样,里面的参数判断的话,蓝牙有各种不同的状态,可以根据搜索到的设备进行检查,然后区分当前蓝牙处于什么状态。而我因为业务原因,暂时也不需要去监听匹配,所以,我用的是以下这个方法:

       Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
            for (BluetoothDevice bondedDevice : bondedDevices) {
                bluetoothDevices.add(bondedDevice);
            }

    当蓝牙开启后,通过此方法,获得已匹配的设备。

    另外补充一个开启蓝牙的方法:此方法为了让其他设备可以搜索到本机,里面的300表示暴露时间秒

     //开启显示,让本机可以被搜索到
                if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
                    Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
                    discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
                    startActivity(discoverableIntent);
                } else {
                    showToast("蓝牙已开启");
                }

     

    好了,以上是一些基本的蓝牙功能,到哪里都可以找到一大堆的。下面开始记录关键,蓝牙数据传输

    既然要传输数据,和给api通讯一样,肯定是要有一个服务端,一个客户端的。而服务端是需要在开启状态,监听客户端发送的数据。而服务属于阻塞操作,所以要开启一个线程,再来开启一个服务:

     BluetoothServerSocket mmServerSocket=mBluetoothAdapter.listenUsingRfcommWithServiceRecord("Name", UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66"));
     BluetoothSocket socket = mmServerSocket.accept();

    通过以上方法来开启一个服务,这里“Name”,UUID,服务端和客户端都必须一样,这是识别用的。另外要注意mmServerSocket 是否为 null ,若未null 则也是无法开启的。另外在之后若出现无法连接的情况,这里我们可以查下缓一缓UUID的值,具体这个坑什么原因我也说不清,反正换一换呗。

    当以上的方法调用了之后,服务器基本就算开启了的。只要没抛异常的话。

    然后我们需要一个输入流,可以写循环,也可以不写循环。循环的话就通过传输过来的数据,做个标记结束它。这些都是一些废话,就直接写个流吧...

     
    InputStream mmInStream = socket.getInputStream();
    
     bytes = mmInStream.read(buffer);
     String data = new String(buffer, 0, bytes, "UTF-8");
     Message msg = Message.obtain();
     msg.what = Constants.MESSAGE_READ;
     msg.obj = data;
     mHandler.sendMessage(msg);

    这里注意,我们在之前开启服务端的时候,要获得一个蓝牙的操作类:BluetoothSocket ,这个类是用来操作流,服务的。ranho然后通过handler 将接收的数据回传到主线程。服务端到这里就完了。

    接着写客户端的:

    客户端这边就轻松多了,既然服务端已经开启,我们就进行连接,通过BluetoothAdapter 我们已经得到了已经匹配的设备,然后选择你要连接的蓝牙设备,不过记得先关闭蓝牙搜索功能:

    mBluetoothAdapter.cancelDiscovery();
    BluetoothSocket mmSocket= device.createRfcommSocketToServiceRecord(UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66"));
     mmSocket.connect();

    然后用要连接的设备device,输入服务端的UUID,接着连接。当然,以上的操作也是需要在子线程中进行的。

    连接结束后,可以写个方法通知服务端,谁谁谁连接了。当然我们也要写一个输出流,用于一会输出数据到服务端去。

    最后,最最重要的,用完了li流一定要记得关闭。

    为了以后方便,我也做了一个功能类,之后需要用时就直接调用。这里就贡献给大家分享一下吧!

    
    public class BluetoothChatService {
    
        private static final String TAG = "BluetoothChatService";
        private static final String NAME_SECURE = "BluetoothChatSecure";
        private static final String NAME_INSECURE = "BluetoothChatInsecure";
    
        private static final UUID MY_UUID_SECURE =
                UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
        public final BluetoothAdapter mAdapter;
        public final Handler mHandler;
        public AcceptThread mSecureAcceptThread;
        public AcceptThread mInsecureAcceptThread;
        public ConnectThread mConnectThread;
        public ConnectedThread mConnectedThread;
        private int mState;
    
        public static final int STATE_NONE = 0;
        public static final int STATE_LISTEN = 1;
        public static final int STATE_CONNECTING = 2;
        public static final int STATE_CONNECTED = 3;
    
    
        public BluetoothChatService(Context context, Handler handler) {
            mAdapter = BluetoothAdapter.getDefaultAdapter();
            mState = STATE_NONE;
            mHandler = handler;
        }
    
    
        private synchronized void setState(int state) {
            Log.d(TAG, "setState() " + mState + " -> " + state);
            mState = state;
            mHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
        }
    
    
        public synchronized int getState() {
            return mState;
        }
    
    
        public synchronized void start() {
            Log.d(TAG, "start");
    
            if (mConnectThread != null) {
                mConnectThread.cancel();
                mConnectThread = null;
            }
    
            if (mConnectedThread != null) {
                mConnectedThread.cancel();
                mConnectedThread = null;
            }
    
            setState(STATE_LISTEN);
    
            if (mSecureAcceptThread == null) {
                mSecureAcceptThread = new AcceptThread();
                mSecureAcceptThread.start();
            }
        }
    
        public synchronized void connect(BluetoothDevice device) {
            Log.d(TAG, "connect to: " + device);
            if (mState == STATE_CONNECTING) {
                if (mConnectThread != null) {
                    mConnectThread.cancel();
                    mConnectThread = null;
                }
            }
    
            if (mConnectedThread != null) {
                mConnectedThread.cancel();
                mConnectedThread = null;
            }
    
            mConnectThread = new ConnectThread(device);
            mConnectThread.start();
            setState(STATE_CONNECTING);
        }
    
    
        public synchronized void connected(BluetoothSocket socket, BluetoothDevice
                device) {
    
            if (mConnectThread != null) {
                mConnectThread.cancel();
                mConnectThread = null;
            }
    
            if (mConnectedThread != null) {
                mConnectedThread.cancel();
                mConnectedThread = null;
            }
    
            if (mSecureAcceptThread != null) {
                mSecureAcceptThread.cancel();
                mSecureAcceptThread = null;
            }
            if (mInsecureAcceptThread != null) {
                mInsecureAcceptThread.cancel();
                mInsecureAcceptThread = null;
            }
    
            mConnectedThread = new ConnectedThread(socket);
            mConnectedThread.start();
    
            Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME);
            Bundle bundle = new Bundle();
            bundle.putString(Constants.DEVICE_NAME, device.getName());
            msg.setData(bundle);
            mHandler.sendMessage(msg);
    
            setState(STATE_CONNECTED);
        }
    
    
        public synchronized void stop() {
            Log.d(TAG, "stop");
    
            if (mConnectThread != null) {
                mConnectThread.cancel();
                mConnectThread = null;
            }
    
            if (mConnectedThread != null) {
                mConnectedThread.cancel();
                mConnectedThread = null;
            }
    
            if (mSecureAcceptThread != null) {
                mSecureAcceptThread.cancel();
                mSecureAcceptThread = null;
            }
    
            if (mInsecureAcceptThread != null) {
                mInsecureAcceptThread.cancel();
                mInsecureAcceptThread = null;
            }
    
            setState(STATE_NONE);
    
        }
    
        public void write(byte[] out) {
            ConnectedThread r;
            synchronized (this) {
                if (mState != STATE_CONNECTED)
                    return;
                r = mConnectedThread;
    
            }
            r.write(out);
        }
    
    
        private void connectionFailed() {
    
            Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);
            Bundle bundle = new Bundle();
            bundle.putString(Constants.TOAST, "无法连接设备!");
            msg.setData(bundle);
            mHandler.sendMessage(msg);
            BluetoothChatService.this.start();
        }
    
    
        private void connectionLost() {
    
            Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);
            Bundle bundle = new Bundle();
            bundle.putString(Constants.TOAST, "设备连接已断开");
            msg.setData(bundle);
            mHandler.sendMessage(msg);
    
            BluetoothChatService.this.start();
        }
    
        private class AcceptThread extends Thread {
            // The local server socket
            private final BluetoothServerSocket mmServerSocket;
            private String mSocketType;
    
            public AcceptThread() {
                BluetoothServerSocket tmp = null;
    
                try {
                        tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, MY_UUID_SECURE);
                } catch (IOException e) {
                   // Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e);
                }
                mmServerSocket = tmp;
            }
    
            public void run() {
    
                BluetoothSocket socket = null;
    
                while (mState != STATE_CONNECTED) {
                    try {
                        socket = mmServerSocket.accept();
                    } catch (IOException e) {
                      //  Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);
                        break;
                    }
                    if (socket != null) {
                        synchronized (BluetoothChatService.this) {
                            switch (mState) {
                                case STATE_LISTEN:
                                case STATE_CONNECTING:
                                    // Situation normal. Start the connected thread.
                                    connected(socket, socket.getRemoteDevice());
                                    break;
                                case STATE_NONE:
                                case STATE_CONNECTED:
                                    try {
                                        socket.close();
                                    } catch (IOException e) {
                                        //Log.e(TAG, "Could not close unwanted socket", e);
                                    }
                                    break;
                            }
                        }
                    }
                }
            }
    
            public void cancel() {
               // Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);
                try {
                    mmServerSocket.close();
                } catch (IOException e) {
                   // Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e);
                }
            }
        }
    
        private class ConnectThread extends Thread {
            private final BluetoothSocket mmSocket;
            private final BluetoothDevice mmDevice;
    
            public ConnectThread(BluetoothDevice device) {
                mmDevice = device;
                BluetoothSocket tmp = null;
    
                try {
                        tmp = device.createRfcommSocketToServiceRecord(
                                MY_UUID_SECURE);
                } catch (IOException e) {
                  //  Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
                }
                mmSocket = tmp;
            }
    
            public void run() {
               // Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
                mAdapter.cancelDiscovery();
    
                try {
                    mmSocket.connect();
                } catch (IOException e) {
    
                    try {
                        mmSocket.close();
                    } catch (IOException e2) {
                     //   Log.e(TAG, "unable to close() " + mSocketType +
                       //         " socket during connection failure", e2);
                    }
                    connectionFailed();
                    return;
                }
    
                synchronized (BluetoothChatService.this) {
                    mConnectThread = null;
                }
    
                connected(mmSocket, mmDevice);
            }
    
            public void cancel() {
                try {
                    mmSocket.close();
                } catch (IOException e) {
                  //  Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
                }
            }
        }
    
        private class ConnectedThread extends Thread {
            private final BluetoothSocket mmSocket;
            private final InputStream mmInStream;
            private final OutputStream mmOutStream;
    
            public ConnectedThread(BluetoothSocket socket) {
               // Log.d(TAG, "create ConnectedThread: " + socketType);
                mmSocket = socket;
                InputStream tmpIn = null;
                OutputStream tmpOut = null;
    
                try {
                    tmpIn = socket.getInputStream();
                    tmpOut = socket.getOutputStream();
                } catch (IOException e) {
                  //  Log.e(TAG, "temp sockets not created", e);
                }
                mmInStream = tmpIn;
                mmOutStream = tmpOut;
            }
    
            public void run() {
    
                synchronized (BluetoothChatService.class) {
    
                    byte[] buffer = new byte[1024 * 4];
                    int bytes;
    
                    while (mState == STATE_CONNECTED) {
                        try {
                            bytes = mmInStream.read(buffer);
                            String data = new String(buffer, 0, bytes, "UTF-8");
                            Message msg = Message.obtain();
                            msg.what = Constants.MESSAGE_READ;
                            msg.obj = data;
                            mHandler.sendMessage(msg);
    
                        } catch (IOException e) {
                            connectionLost();
                            BluetoothChatService.this.start();
                            break;
                        }
                    }
                }
            }
    
            public void write(byte[] buffer) {
    
                try {
                    mmOutStream.write(buffer);
                    mmOutStream.flush();
                    mHandler.obtainMessage(Constants.MESSAGE_WRITE, -1, -1, buffer)
                            .sendToTarget();
                } catch (IOException e) {
                    //Log.e(TAG, "Exception during write", e);
                }
            }
    
            public void cancel() {
                try {
                    mmSocket.close();
                } catch (IOException e) {
                //    Log.e(TAG, "close() of connect socket failed", e);
                }
            }
        }
    }
    
    /**
     * @desc ${BluetoothService 中使用的常量}
     */
    public class Constants {
    
        public static final int MESSAGE_STATE_CHANGE = 1;  //状态改变
        public static final int MESSAGE_READ = 2; //读取
        public static final int MESSAGE_WRITE = 3;   //写出
        public static final int MESSAGE_DEVICE_NAME = 4;  //设备 名称
        public static final int MESSAGE_TOAST = 5;  //连接失败
    
    
        public static final String DEVICE_NAME = "device_name";
        public static final String TOAST = "toast";
    
    }
     private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case Constants.MESSAGE_TOAST:
                        String statu = msg.getData().getString(Constants.TOAST);
                        showToast(statu + "");
                        break;
                    case Constants.MESSAGE_DEVICE_NAME:
                        String string = msg.getData().getString(Constants.DEVICE_NAME);
                        showToast("已连接设备" + string);
                        break;
                    case Constants.MESSAGE_READ: //读取接收数据
          
                        break;
                    case Constants.MESSAGE_WRITE: //发送端,正在发送的数据
                       
                        break;
    
                }
            }
      };
    
    private void iniData(){
    省******
    ******
    ******略
      BluetoothChatService chatService = new BluetoothChatService(getActivity(), mHandler);
    
      Listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long        id) {
                    // 判断当前是否还是正在搜索周边设备,如果是则暂停搜索
                    if (mBluetoothAdapter.isDiscovering()) {
                        mBluetoothAdapter.cancelDiscovery();
                    }
                    BluetoothDevice bluetoothDevice = bluetoothDevices.get(position);
                    //客户端连接
                    chatService.connect(bluetoothDevice);
                }
            });
    
    //如果要开启服务端直接调用以下语句
    //chatService.start();
    
    }
    
      /**
         * 准备开始发送数据
         */
        private void snedInfo() {
            if (chatService.getState() != BluetoothChatService.STATE_CONNECTED) {
                showToast("连接失败");
                return;
            }
          String toSendJson = new Gson().toJson(“Bean数据”);
           if (toSendJson.length() > 0) {
                byte[] send = new byte[0];
                 try {
                   send = toSendJson.getBytes();
                  } catch (Exception e) {
                   e.printStackTrace();
                  }
                   chatService.write(send);
                  }
                }
            });
        }

    以上就是我目前做的蓝牙传输功能了,先这样吧...然后再慢慢优化改进的。各种状态也都有了的,自己慢慢调整一下吧..

    展开全文
  • 苹果的这个限制措施是比较厉害的,它在iPhone的设计开发时就采用了独立的固件系统,这个固件本身就支持文件传输系统,也能与目前市面上的其他手机兼容蓝牙系统,所以无法向其他手机传输任何文档内容。...

    苹果设备的蓝牙只支持跟蓝牙耳机配对和进行某些游戏的联机对战

    苹果的这个限制措施是比较厉害的,它在iPhone的设计开发时就采用了独立的固件系统,这个固件本身就不支持文件传输系统,也不能与目前市面上的其他手机兼容蓝牙系统,所以无法向其他手机传输任何文档内容。

    下面介绍几款可以利用iPhone蓝牙功能实现文件传输的应用: 

          Bluetooth Photo Share:该应用也能实现iPhone,iPod touch或ipad之间进行照片传输,其操作简单方便界面友好,传输快速实用需要3.0以上固件和2代以上的机器支持。Bluetooth Photo Share是免费应用。

    转载于:https://www.cnblogs.com/iusmile/archive/2012/10/10/2717651.html

    展开全文
  • Android蓝牙实现设备间数据传输

    万次阅读 2017-07-31 21:28:41
    这篇文章总结通过蓝牙实现两台pad间数据传输 文章基于传统蓝牙,而不是BLE。 蓝牙应该是移动设备经常用到的功能模块,Android Bluetooth API 是Android提供的用来实现点到点和多点无线功能。 使用 Bluetooth API,...

    这篇文章总结通过蓝牙实现两台pad间数据传输

    文章基于传统蓝牙,而不是BLE
    蓝牙应该是移动设备经常用到的功能模块,Android Bluetooth API 是Android提供的用来实现点到点和多点无线功能。
    使用 Bluetooth API,Android 应用可执行以下操作:

    • 扫描其他蓝牙设备
    • 查询本地蓝牙适配器的配对蓝牙设备
    • 建立 RFCOMM 通道
    • 通过服务发现连接到其他设备
    • 与其他设备进行双向数据传输
    • 管理多个连接

    蓝牙权限

    <manifest ... >
      <uses-permission android:name="android.permission.BLUETOOTH" />
      <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
      ...
    </manifest>

    1.设置蓝牙

    您需要验证设备支持蓝牙,并且如果支持确保将其启用,然后您的应用才能通过蓝牙进行通信。

    • 获取 BluetoothAdapter
      所有蓝牙 Activity 都需要 BluetoothAdapter。

      BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
      if (mBluetoothAdapter == null) {
      // Device does not support Bluetooth
      }
    • 启用蓝牙
      下一步,您需要确保已启用蓝牙。

      if (!mBluetoothAdapter.isEnabled()) {
      Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
      startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
      }

    2.搜索蓝牙设备

    要开始发现设备,只需调用 startDiscovery()。该进程为异步进程,并且该方法会立即返回一个布尔值,指示是否已成功启动发现操作。 发现进程通常包含约 12 秒钟的查询扫描,之后对每台发现的设备进行页面扫描,以检索其蓝牙名称。

    /**
         * 发现蓝牙的广播
         */
        private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
    
                String action = intent.getAction();
                if(BluetoothDevice.ACTION_FOUND.equals(action)){
                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    //todo处理搜索到的蓝牙设备
                }
            }
        };

    在Activity或者Fragment中注册广播

            IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
            registerReceiver(mReceiver, filter);

    3.连接设备

    要在两台设备上的应用之间创建连接,必须同时实现服务器端和客户端机制,因为其中一台设备必须开放服务器套接字,而另一台设备必须发起连接(使用服务器设备的 MAC 地址发起连接)。
    服务器设备和客户端设备分别以不同的方法获得需要的 BluetoothSocket。服务器将在传入连接被接受时收到套接字。 客户端将在其打开到服务器的 RFCOMM 通道时收到该套接字。

    注:如果两台设备之前尚未配对,则在连接过程中,Android 框架会自动向用户显示配对请求通知或对话框。因此,在尝试连接设备时,您的应用无需担心设备是否已配对。 您的 RFCOMM 连接尝试将被阻塞,直至用户成功完成配对或配对失败(包括用户拒绝配对、配对失败或超时)。

    • 连接为服务器
      当您需要连接两台设备时,其中一台设备必须通过保持开放的 BluetoothServerSocket 来充当服务器。
      • 1.通过调用 listenUsingRfcommWithServiceRecord(String, UUID) 获取 BluetoothServerSocket。
        当客户端尝试连接此设备时,它会携带能够唯一标识其想要连接的服务的 UUID。 两个 UUID 必须匹配,在下一步中,连接才会被接受。
      • 通过调用 accept() 开始侦听连接请求。
      • 除非您想要接受更多连接,否则请调用 close()。
        这将释放服务器套接字及其所有资源,但不会关闭 accept() 所返回的已连接的 BluetoothSocket。 与 TCP/IP 不同,RFCOMM 一次只允许每个通道有一个已连接的客户端,因此大多数情况下,在接受已连接的套接字后立即在 BluetoothServerSocket 上调用 close() 是行得通的。

    以下是一个用于接受传入连接的服务器组件的简化线程:

    private class AcceptThread extends Thread {
        private final BluetoothServerSocket mmServerSocket;
    
        public AcceptThread() {
            // Use a temporary object that is later assigned to mmServerSocket,
            // because mmServerSocket is final
            BluetoothServerSocket tmp = null;
            try {
                // MY_UUID is the app's UUID string, also used by the client code
                tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
            } catch (IOException e) { }
            mmServerSocket = tmp;
        }
    
        public void run() {
            BluetoothSocket socket = null;
            // Keep listening until exception occurs or a socket is returned
            while (true) {
                try {
                    socket = mmServerSocket.accept();
                } catch (IOException e) {
                    break;
                }
                // If a connection was accepted
                if (socket != null) {
                    // Do work to manage the connection (in a separate thread)
                    manageConnectedSocket(socket);
                    mmServerSocket.close();
                    break;
                }
            }
        }
    
        /** Will cancel the listening socket, and cause the thread to finish */
        public void cancel() {
            try {
                mmServerSocket.close();
            } catch (IOException e) { }
        }
    }

    注意上面是简化线程,也是网上搜索到最多的方法。但是其稳定性真的很差劲。

    • 连接为客户端
      要发起与远程设备(保持开放的服务器套接字的设备)的连接,必须首先获取表示该远程设备的 BluetoothDevice 对象。(在前面有关查找设备的部分介绍了如何获取 BluetoothDevice)。 然后必须使用 BluetoothDevice 来获取 BluetoothSocket 并发起连接。
      • 1.使用 BluetoothDevice,通过调用 createRfcommSocketToServiceRecord(UUID) 获取 BluetoothSocket。
      • 2.通过调用 connect() 发起连接。

    以下是发起蓝牙连接的线程的基本示例:

    private class ConnectThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final BluetoothDevice mmDevice;
    
        public ConnectThread(BluetoothDevice device) {
            // Use a temporary object that is later assigned to mmSocket,
            // because mmSocket is final
            BluetoothSocket tmp = null;
            mmDevice = device;
    
            // Get a BluetoothSocket to connect with the given BluetoothDevice
            try {
                // MY_UUID is the app's UUID string, also used by the server code
                tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
            } catch (IOException e) { }
            mmSocket = tmp;
        }
    
        public void run() {
            // Cancel discovery because it will slow down the connection
            mBluetoothAdapter.cancelDiscovery();
    
            try {
                // Connect the device through the socket. This will block
                // until it succeeds or throws an exception
                mmSocket.connect();
            } catch (IOException connectException) {
                // Unable to connect; close the socket and get out
                try {
                    mmSocket.close();
                } catch (IOException closeException) { }
                return;
            }
    
            // Do work to manage the connection (in a separate thread)
            manageConnectedSocket(mmSocket);
        }
    
        /** Will cancel an in-progress connection, and close the socket */
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) { }
        }
    }

    代码很容易看懂,就不做过多解释了。


    4.管理连接

    在成功连接两台(或更多台)设备后,每台设备都会有一个已连接的 BluetoothSocket。 这一点非常有趣,因为这表示您可以在设备之间共享数据。 利用 BluetoothSocket,传输任意数据的一般过程非常简单:

    • 获取 InputStream 和 OutputStream,二者分别通过套接字以及 getInputStream() 和 getOutputStream() 来处理数据传输。
    • 使用 read(byte[]) 和 write(byte[]) 读取数据并写入到流式传输。

    ok,至此,我们基本了解了传统蓝牙数据传输常用的API以及一些核心API的使用,接下来具体看我在项目中如何处理数据传输以及解决蓝牙Socket的稳定性。
    在了解了上面一套机制以后,最开始我也是使用上面的简单实例进行蓝牙数据的传输与接受,但在传输过程中经常会出现连接异常,Socket断开等各种异常。经过不断的寻找解决方案,最后发现了官方一个通过蓝牙进行设备聊天的demo,仔细研究发现主要是通过一个自己封装的BluetoothChatService,其内部本质还是使用的我们上面介绍的知识,只不过这个Service对连接的处理更详细。

    public class BluetoothChatService {
        // Debugging
        private static final String TAG = "BluetoothChatService";
    
        // Name for the SDP record when creating server socket
        private static final String NAME_SECURE = "BluetoothChatSecure";
        private static final String NAME_INSECURE = "BluetoothChatInsecure";
    
        // Unique UUID for this application
        private static final UUID MY_UUID_SECURE =
                UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
        private static final UUID MY_UUID_INSECURE =
                UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66");
    
        // Member fields
        private final BluetoothAdapter mAdapter;
        private final Handler mHandler;
        private AcceptThread mSecureAcceptThread;
        private AcceptThread mInsecureAcceptThread;
        private ConnectThread mConnectThread;
        private ConnectedThread mConnectedThread;
        private int mState;
        private int mNewState;
    
        // Constants that indicate the current connection state
        public static final int STATE_NONE = 0;       // we're doing nothing
        public static final int STATE_LISTEN = 1;     // now listening for incoming connections
        public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
        public static final int STATE_CONNECTED = 3;  // now connected to a remote device
    
        /**
         * Constructor. Prepares a new BluetoothChat session.
         *
         * @param context The UI Activity Context
         * @param handler A Handler to send messages back to the UI Activity
         */
        public BluetoothChatService(Context context, Handler handler) {
            mAdapter = BluetoothAdapter.getDefaultAdapter();
            mState = STATE_NONE;
            mNewState = mState;
            mHandler = handler;
        }
    
        /**
         * Update UI title according to the current state of the chat connection
         */
        private synchronized void updateUserInterfaceTitle() {
            mState = getState();
            Log.d(TAG, "updateUserInterfaceTitle() " + mNewState + " -> " + mState);
            mNewState = mState;
    
            // Give the new state to the Handler so the UI Activity can update
            mHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, mNewState, -1).sendToTarget();
        }
    
        /**
         * Return the current connection state.
         */
        public synchronized int getState() {
            return mState;
        }
    
        /**
         * Start the chat service. Specifically start AcceptThread to begin a
         * session in listening (server) mode. Called by the Activity onResume()
         */
        public synchronized void start() {
            Log.d(TAG, "start");
    
            // Cancel any thread attempting to make a connection
            if (mConnectThread != null) {
                mConnectThread.cancel();
                mConnectThread = null;
            }
    
            // Cancel any thread currently running a connection
            if (mConnectedThread != null) {
                mConnectedThread.cancel();
                mConnectedThread = null;
            }
    
            // Start the thread to listen on a BluetoothServerSocket
            if (mSecureAcceptThread == null) {
                mSecureAcceptThread = new AcceptThread(true);
                mSecureAcceptThread.start();
            }
            if (mInsecureAcceptThread == null) {
                mInsecureAcceptThread = new AcceptThread(false);
                mInsecureAcceptThread.start();
            }
            // Update UI title
            updateUserInterfaceTitle();
        }
    
        /**
         * Start the ConnectThread to initiate a connection to a remote device.
         *
         * @param device The BluetoothDevice to connect
         * @param secure Socket Security type - Secure (true) , Insecure (false)
         */
        public synchronized void connect(BluetoothDevice device, boolean secure) {
            Log.d(TAG, "connect to: " + device);
    
            // Cancel any thread attempting to make a connection
            if (mState == STATE_CONNECTING) {
                if (mConnectThread != null) {
                    mConnectThread.cancel();
                    mConnectThread = null;
                }
            }
    
            // Cancel any thread currently running a connection
            if (mConnectedThread != null) {
                mConnectedThread.cancel();
                mConnectedThread = null;
            }
    
            // Start the thread to connect with the given device
            mConnectThread = new ConnectThread(device, secure);
            mConnectThread.start();
            // Update UI title
            updateUserInterfaceTitle();
        }
    
        /**
         * Start the ConnectedThread to begin managing a Bluetooth connection
         *
         * @param socket The BluetoothSocket on which the connection was made
         * @param device The BluetoothDevice that has been connected
         */
        public synchronized void connected(BluetoothSocket socket, BluetoothDevice
                device, final String socketType) {
            Log.d(TAG, "connected, Socket Type:" + socketType);
    
            // Cancel the thread that completed the connection
            if (mConnectThread != null) {
                mConnectThread.cancel();
                mConnectThread = null;
            }
    
            // Cancel any thread currently running a connection
            if (mConnectedThread != null) {
                mConnectedThread.cancel();
                mConnectedThread = null;
            }
    
            // Cancel the accept thread because we only want to connect to one device
            if (mSecureAcceptThread != null) {
                mSecureAcceptThread.cancel();
                mSecureAcceptThread = null;
            }
            if (mInsecureAcceptThread != null) {
                mInsecureAcceptThread.cancel();
                mInsecureAcceptThread = null;
            }
    
            // Start the thread to manage the connection and perform transmissions
            mConnectedThread = new ConnectedThread(socket, socketType);
            mConnectedThread.start();
    
            // Send the name of the connected device back to the UI Activity
            Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME);
            Bundle bundle = new Bundle();
            bundle.putString(Constants.DEVICE_NAME, device.getName());
            msg.setData(bundle);
            mHandler.sendMessage(msg);
            // Update UI title
            updateUserInterfaceTitle();
        }
    
        /**
         * Stop all threads
         */
        public synchronized void stop() {
            Log.d(TAG, "stop");
    
            if (mConnectThread != null) {
                mConnectThread.cancel();
                mConnectThread = null;
            }
    
            if (mConnectedThread != null) {
                mConnectedThread.cancel();
                mConnectedThread = null;
            }
    
            if (mSecureAcceptThread != null) {
                mSecureAcceptThread.cancel();
                mSecureAcceptThread = null;
            }
    
            if (mInsecureAcceptThread != null) {
                mInsecureAcceptThread.cancel();
                mInsecureAcceptThread = null;
            }
            mState = STATE_NONE;
            // Update UI title
            updateUserInterfaceTitle();
        }
    
        /**
         * Write to the ConnectedThread in an unsynchronized manner
         *
         * @param out The bytes to write
         * @see ConnectedThread#write(byte[])
         */
        public void write(byte[] out) {
            // Create temporary object
            ConnectedThread r;
            // Synchronize a copy of the ConnectedThread
            synchronized (this) {
                if (mState != STATE_CONNECTED) return;
                r = mConnectedThread;
            }
            // Perform the write unsynchronized
            r.write(out);
        }
    
        /**
         * Indicate that the connection attempt failed and notify the UI Activity.
         */
        private void connectionFailed() {
            // Send a failure message back to the Activity
            Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);
            Bundle bundle = new Bundle();
            bundle.putString(Constants.TOAST, "无法连接蓝牙设备...");
            msg.setData(bundle);
            mHandler.sendMessage(msg);
    
            mState = STATE_NONE;
            // Update UI title
            updateUserInterfaceTitle();
    
            // Start the service over to restart listening mode
            BluetoothChatService.this.start();
        }
    
        /**
         * Indicate that the connection was lost and notify the UI Activity.
         */
        private void connectionLost() {
            // Send a failure message back to the Activity
            Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);
            Bundle bundle = new Bundle();
            bundle.putString(Constants.TOAST, "Device connection was lost");
            msg.setData(bundle);
            mHandler.sendMessage(msg);
    
            mState = STATE_NONE;
            // Update UI title
            updateUserInterfaceTitle();
    
            // Start the service over to restart listening mode
            BluetoothChatService.this.start();
        }
    
        /**
         * This thread runs while listening for incoming connections. It behaves
         * like a server-side client. It runs until a connection is accepted
         * (or until cancelled).
         */
        private class AcceptThread extends Thread {
            // The local server socket
            private final BluetoothServerSocket mmServerSocket;
            private String mSocketType;
    
            public AcceptThread(boolean secure) {
                BluetoothServerSocket tmp = null;
                mSocketType = secure ? "Secure" : "Insecure";
    
                // Create a new listening server socket
                try {
                    if (secure) {
                        tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,
                                MY_UUID_SECURE);
                    } else {
                        tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(
                                NAME_INSECURE, MY_UUID_INSECURE);
                    }
                } catch (IOException e) {
                    Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e);
                }
                mmServerSocket = tmp;
                mState = STATE_LISTEN;
            }
    
            public void run() {
                Log.d(TAG, "Socket Type: " + mSocketType +
                        "BEGIN mAcceptThread" + this);
                setName("AcceptThread" + mSocketType);
    
                BluetoothSocket socket = null;
    
                // Listen to the server socket if we're not connected
                while (mState != STATE_CONNECTED) {
                    try {
                        // This is a blocking call and will only return on a
                        // successful connection or an exception
                        socket = mmServerSocket.accept();
                    } catch (IOException e) {
                        Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);
                        break;
                    }
    
                    // If a connection was accepted
                    if (socket != null) {
                        synchronized (BluetoothChatService.this) {
                            switch (mState) {
                                case STATE_LISTEN:
                                case STATE_CONNECTING:
                                    // Situation normal. Start the connected thread.
                                    connected(socket, socket.getRemoteDevice(),
                                            mSocketType);
                                    break;
                                case STATE_NONE:
                                case STATE_CONNECTED:
                                    // Either not ready or already connected. Terminate new socket.
                                    try {
                                        socket.close();
                                    } catch (IOException e) {
                                        Log.e(TAG, "Could not close unwanted socket", e);
                                    }
                                    break;
                            }
                        }
                    }
                }
                Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);
    
            }
    
            public void cancel() {
                Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);
                try {
                    mmServerSocket.close();
                } catch (IOException e) {
                    Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e);
                }
            }
        }
    
    
        /**
         * This thread runs while attempting to make an outgoing connection
         * with a device. It runs straight through; the connection either
         * succeeds or fails.
         */
        private class ConnectThread extends Thread {
            private final BluetoothSocket mmSocket;
            private final BluetoothDevice mmDevice;
            private String mSocketType;
    
            public ConnectThread(BluetoothDevice device, boolean secure) {
                mmDevice = device;
                BluetoothSocket tmp = null;
                mSocketType = secure ? "Secure" : "Insecure";
    
                // Get a BluetoothSocket for a connection with the
                // given BluetoothDevice
                try {
                    if (secure) {
                        tmp = device.createRfcommSocketToServiceRecord(
                                MY_UUID_SECURE);
                    } else {
                        tmp = device.createInsecureRfcommSocketToServiceRecord(
                                MY_UUID_INSECURE);
                    }
                } catch (IOException e) {
                    Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
                }
                mmSocket = tmp;
                mState = STATE_CONNECTING;
            }
    
            public void run() {
                Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
                setName("ConnectThread" + mSocketType);
    
                // Always cancel discovery because it will slow down a connection
                mAdapter.cancelDiscovery();
    
                // Make a connection to the BluetoothSocket
                try {
                    // This is a blocking call and will only return on a
                    // successful connection or an exception
                    mmSocket.connect();
                } catch (IOException e) {
                    // Close the socket
                    try {
                        mmSocket.close();
                    } catch (IOException e2) {
                        Log.e(TAG, "unable to close() " + mSocketType +
                                " socket during connection failure", e2);
                    }
                    connectionFailed();
                    return;
                }
    
                // Reset the ConnectThread because we're done
                synchronized (BluetoothChatService.this) {
                    mConnectThread = null;
                }
    
                // Start the connected thread
                connected(mmSocket, mmDevice, mSocketType);
            }
    
            public void cancel() {
                try {
                    mmSocket.close();
                } catch (IOException e) {
                    Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
                }
            }
        }
    
        /**
         * This thread runs during a connection with a remote device.
         * It handles all incoming and outgoing transmissions.
         */
        private class ConnectedThread extends Thread {
            private final BluetoothSocket mmSocket;
            private final InputStream mmInStream;
            private final OutputStream mmOutStream;
    
            public ConnectedThread(BluetoothSocket socket, String socketType) {
                Log.d(TAG, "create ConnectedThread: " + socketType);
                mmSocket = socket;
                InputStream tmpIn = null;
                OutputStream tmpOut = null;
    
                // Get the BluetoothSocket input and output streams
                try {
                    tmpIn = socket.getInputStream();
                    tmpOut = socket.getOutputStream();
                } catch (IOException e) {
                    Log.e(TAG, "temp sockets not created", e);
                }
    
                mmInStream = tmpIn;
                mmOutStream = tmpOut;
                mState = STATE_CONNECTED;
            }
    
            public void run() {
                Log.i(TAG, "BEGIN mConnectedThread");
                byte[] buffer = new byte[1024];
                int bytes;
    
                // Keep listening to the InputStream while connected
                while (mState == STATE_CONNECTED) {
                    try {
                        // Read from the InputStream
                        bytes = mmInStream.read(buffer);
    
                        // Send the obtained bytes to the UI Activity
                        mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
                                .sendToTarget();
                    } catch (IOException e) {
                        Log.e(TAG, "disconnected", e);
                        connectionLost();
                        break;
                    }
                }
            }
    
            /**
             * Write to the connected OutStream.
             *
             * @param buffer The bytes to write
             */
            public void write(byte[] buffer) {
                try {
                    mmOutStream.write(buffer);
    
                    // Share the sent message back to the UI Activity
                    mHandler.obtainMessage(Constants.MESSAGE_WRITE, -1, -1, buffer)
                            .sendToTarget();
                } catch (IOException e) {
                    Log.e(TAG, "Exception during write", e);
                }
            }
    
            public void cancel() {
                try {
                    mmSocket.close();
                } catch (IOException e) {
                    Log.e(TAG, "close() of connect socket failed", e);
                }
            }
        }
    }

    看似很长的一段代码,其实仔细阅读并不难。提供了三个线程:

    • AcceptThread 用来处理接收数据;
    • ConnectThread 用来处理蓝牙设备连接;
    • ConnectedThread 用来管理连接,进行数据传输。

    接下来看具体的使用:

    发送方:

    private final Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what){
                    case Constants.MESSAGE_WRITE:
                        T.showLong(MshipWaterTankActivity.this,"蓝牙数据发送成功!");
                        break;
                    case Constants.MESSAGE_READ:
                        //处理接收到的数据
                        break;
                    case Constants.MESSAGE_DEVICE_NAME:
                        try {
                            // save the connected device's name
                            mConnectedDeviceName = msg.getData().getString(Constants.DEVICE_NAME);
                            if (null != mContext) {
                                Toast.makeText(mContext, "蓝牙已成功连接到: "
                                        + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
                            }
                            //连接成功发送数据
                            sendMsg();
                        } catch (Exception e) {
                            e.printStackTrace();
                            Log.e("wj","压缩数据异常");
                        }
                        break;
                    case Constants.MESSAGE_TOAST:
                        if (null != mContext) {
                            Toast.makeText(mContext, msg.getData().getString(Constants.TOAST),
                                    Toast.LENGTH_SHORT).show();
                        }
                        break;
                }
            }
        };
    @Override
        protected void onStart() {
            super.onStart();
            // If BT is not on, request that it be enabled.
            // setupChat() will then be called during onActivityResult
            if (!mBluetoothAdapter.isEnabled()) {
                Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
                // Otherwise, setup the chat session
            } else if (mChatService == null) {
                setupChat();
            }
        }
    
         @Override
        protected void onResume() {
            super.onResume();
    
            // Performing this check in onResume() covers the case in which BT was
            // not enabled during onStart(), so we were paused to enable it...
            // onResume() will be called when ACTION_REQUEST_ENABLE activity returns.
            if (mChatService != null) {
                // Only if the state is STATE_NONE, do we know that we haven't started already
                if (mChatService.getState() == BluetoothChatService.STATE_NONE) {
                    // Start the Bluetooth chat services
                    mChatService.start();
                }
            }
    
    @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            switch (requestCode){
                case REQUEST_ENABLE_BT:
                    // When the request to enable Bluetooth returns
                    if (resultCode == Activity.RESULT_OK) {
                        // Bluetooth is now enabled, so set up a chat session
                        setupChat();
                    } else {
                        // User did not enable Bluetooth or an error occurred
                        Log.d("wj", "BT not enabled");
                        Toast.makeText(this, "蓝牙会话已关闭",
                                Toast.LENGTH_SHORT).show();
                    }
                    break;
            }
        }
    @Override
        protected void onDestroy() {
            super.onDestroy();
            unregisterReceiver(mReceiver);
            if (mChatService != null) {
                mChatService.stop();
            }
        }
    private void setupChat(){
            // Initialize the BluetoothChatService to perform bluetooth connections
            mChatService = new BluetoothChatService(this, mHandler);
            // Initialize the buffer for outgoing messages
            mOutStringBuffer = new StringBuffer("");
        }
    
    private void connectDevice(BluetoothDevice device, boolean secure) {
            // Attempt to connect to the device
            mChatService.connect(device, secure);
        }
    private void sendBluetoothMessage(String message) {
            // Check that we're actually connected before trying anything
            if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) {
                Toast.makeText(this, "蓝牙未连接...", Toast.LENGTH_SHORT).show();
                return;
            }
    
            // Check that there's actually something to send
            if (message.length() > 0) {
                // Get the message bytes and tell the BluetoothChatService to write
                byte[] send = message.getBytes();
                mChatService.write(send);
    
                // Reset out string buffer to zero and clear the edit text field
                mOutStringBuffer.setLength(0);
            }
        }
        ...

    代码并不是很全,但是可以看到关键的Service的初始化,设备连接的方法以及传输数据的方法。

    接收方:

    其实跟上面的类似,只不过BluetoothChatService处理的handler不同,这里就不再继续赘述上面各种Service的初始化,看下核心handler的处理:

    private final Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                FragmentActivity activity = getActivity();
                switch (msg.what) {
                    case Constants.MESSAGE_READ:
                        byte[] readBuf = (byte[]) msg.obj;
                        // construct a string from the valid bytes in the buffer
                        String readMessage = new String(readBuf, 0, msg.arg1);
                        dealContent(readMessage);//处理返回的数据
                        break;
                    case Constants.MESSAGE_DEVICE_NAME:
                        // save the connected device's name
                        mConnectedDeviceName = msg.getData().getString(Constants.DEVICE_NAME);
                        if (null != activity) {
                            Toast.makeText(activity, "已成功连接到 "
                                    + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
                        }
                        break;
                    case Constants.MESSAGE_TOAST:
                        if (null != activity) {
                            Toast.makeText(activity, msg.getData().getString(Constants.TOAST),
                                    Toast.LENGTH_SHORT).show();
                        }
                        break;
                }
            }
        };

    可以看到dealContent(readMessage)方法就是处理接收到的数据。

    细心的你可能发现了,我在进行蓝牙数据传输的时候对字符串进行了压缩处理,因为我传输的json字符串超过了蓝牙数据传输的最大限制,导致接收到的数据不完整,因此我对数据进行了压缩和解压缩处理。


    通过使用BluetoothChatService,可以很好的解决两台设备间数据传输的问题。具体可以参考官方蓝牙聊天的demo:https://github.com/googlesamples/android-BluetoothChat/#readme

    这篇文章使用的是传统蓝牙的技术,Android4.0以后推出了BLE蓝牙机制。开始我也下尝试使用BLE实现两台平板间数据传输。但是仔细深入了解以后,BLE分外围设备和中央设备,通常我们手机连接手表或者一些智能设备都使用BLE的方式进行的数据传输。但是手机和手机通过BLE方式传输会有一个问题,需要我们自己将其中一台设备通过API处理成模拟外围设备,但是我在将一台设备模拟成外围设备进行数据传输的时候还是无法成功发送数据,网上很多地方说Android自身不支持手机和手机间通过BLE传输数据,但我也没有在官网找到明确的说法,总之 最终我放弃了BLE,还是用传统蓝牙实现了设备间数据传输。

    接下来有时间我会继续对BLE的机制进行总结。欢迎大家留言交流。

    展开全文
  •  平时我们用蓝牙耳机听音乐,和不同的设备共享文件,打电话等,都有一个配对--连接--传输数据的过程。  蓝牙配对过程,其实就是一个的过程。  为什么不配对便无法建立连接?  任何无线通信技术都存在被监听和...
  • 蓝牙配对和连接的建立过程

    千次阅读 2019-09-27 09:58:41
    平时我们用蓝牙耳机听音乐,和不同的设备共享文件,打电话等,都有一个配对--连接--传输数据的过程。 配对,其实就是一个认证的过程。 1. 为什么不配对便无法建立连接? 任何无线通信技术都存在被监听和破解的...

    蓝牙的建立过程是一个复杂的过程,即使有过相当一段工作和使用经验的人,如果不仔细去了解还是理解不全。

    平时我们用蓝牙耳机听音乐,和不同的设备共享文件,打电话等,都有一个配对--连接--传输数据的过程。

    配对,其实就是一个认证的过程。

     

    1. 为什么不配对便无法建立连接?

    任何无线通信技术都存在被监听和破解的可能,蓝牙SIG为了保证蓝牙通信的安全性,采用认证的方式进行数据交互。同时为了保证使用的方便性,以配对的形式完成两个蓝牙设备之间的首次通讯认证,经配对之后,随后的通讯连接就不必每次都要做确认。所以认证码的产生是从配对开始的,经过配对,设备之间以PIN码建立约定的link key用于产生初始认证码,以用于以后建立的连接。

    所以不配对,两个设备之间便无法建立认证关系,无法进行连接及其之后的操作,所以配对在一定程度上保证了蓝牙通信的安全,当然这个安全保证机制是比较容易被破解的,因为现在很多个人设备没有人机接口,所以PIN码都是固定的而且大都设置为通用的0000或者1234之类的,所以很容易被猜到并进而建立配对和连接。

     

    2. 蓝牙的连接过程

    现在的蓝牙芯片供应商提供的技术支持能力相当强大,有完整的硬件和软件解决方案。对于应用而言,提供了固件用于实现底层协议栈,提供了profile库及源代码规范了各种应用,开发人员只要专注于应用程序开发就可以了。对于蓝牙底层的一些东西往往不甚了了。以前我也是这样子的,最近在做一个自动搜索以实现自动连接的应用,发现还是需要了解一些底层的机制的。
    我们可以很容易的进行操作在一个手机和免提设备之间建立连接,那么这个连接是怎么建立起来的呢?
    首先,主设备(master,即发起连接的设备)会寻呼(page)从设备(slave,接收连接的设备),master会已跳频的方式去寻呼slave,slave会固定间隔地去扫描(scan)外部寻呼,即page scan,当scan 到外部page时便会响应response该page,这样两个设备之间便会建立link的连接,即ACL链路的连接。当ACL 链路连接建立后,主设备会发起channel的连接请求,即L2CAP的连接,建立L2CAP的连接之后,主设备采用SDP去查询从设备的免提服务,从中得到rfcomm的通道号,然后主设备会发起rfcomm的连接请求建立rfcomm的连接。然后就建立了应用的连接。
    即link establish->channel establish->rfcomm establish->connection

     

    3. 广播数据分析

    3.1. 发送广播数据包的叫广播发起者(advertisers),在广播通道接收广播数据包但没意向连接广播发起设备的叫扫描者( scanners), 需要连接到另一个设备的设备叫做 initiators,它监听可连接的广播数据包。如果advertiser正在使用一个可连接的广播事件, initiator在收到连接数据包的物理通道上发起一个连接请求,如果advertiser接受这个连接请求则这个广播事件结束,并且开始一个新的连接事件。一旦连接建立,initiator成为主设备,advertiser成为从设备。连接事件被用于在主从设备之间传输数据包。
    连接过程:
    广播者:广播包使用ADV_IND PDU标志;
    扫描者:发送扫描请求(SCAN_REQ PDU)请求关于广播者的信息一个SCAN_REQ PDU(包含了扫描者的设备地 址), 在同一信道上回复一个SCAN_RSP PDU;
    发起者: 发起者发送连接请求(CONNECT_REQ PDU)请求进入连接态,广播者确认即可以连接上。

            SCAN_REQ_PDU载荷如下图所示,由ScanA(扫描设备地址)和AdvA组成(广播设备地址),ScanA是扫描设备的公共或随机地址(由TxAdd确定),AdvA是广播设备的公共或随机地址(由RxAdd确定)。

                                                                                   图 -  扫描请求PDU载荷

      广播报文的报头中的TxAdd指示了扫描设备使用的是公共地址(Public Address)还是随机地址(Random Address)。

    TxAdd = 0:公共地址。
    TxAdd = 1:随机地址。
      RxAdd指示了广播设备使用的是公共地址(Public Address)还是随机地址(Random Address)。

    RxAdd = 0:公共地址。
    RxAdd = 1:随机地址。
    3.2. 扫描响应

      SCAN_RSQ_PDU载荷如下图所示,由AdvA(广播设备地址)和ScanRspData组成(扫描响应数据),AdvA是广播设备的公共或随机地址(由TxAdd确定)。

                                                                               图 - 扫描响应PDU载荷

      广播报文的报头中的TxAdd指示了广播设备使用的是公共地址(Public Address)还是随机地址(Random Address)。

    TxAdd = 0:公共地址。
    TxAdd = 1:随机地址。
      广播报文的长度域指示了载荷的字节数(AdvA和ScanRspData)。

    3.3. SCAN_REQ和SCAN_RSP解析

    3.3.1. 捕获SCAN_REQ

      按照《蓝牙4.0BLE抓包(一)》中的描述进行抓包,下面是我们捕获一个心率计的SCAN_REQ包。

                                                       图4:捕获的SCAN_REQ包

    3.3.2. 分析SCAN_REQ

      为了方便分析,我们先取出这个SCAN_REQ包实际传输的数据,如图3中所示。心率计完整的广播报文如下:

      D6 BE 89 8E 83 0C 7F 0F 72 DD DF 68 DA B5 E9 D2 CC F3 BD BF 27

      在分析数据之前,再次说明:广播包含扫描请求和扫描响应,所以扫描请求和扫描响应得包格式遵循广播包的格式。

         分析报文时,需要注意一下报文各个域的字节序。

    3.3.2.1. 接入地址

      D6 BE 89 8E:接入地址,对广播来说是固定值。注意一下这里的字节序,接入地址传输时是低字节在前的。

    3.3.2.2. PDU

        1). 83:广播报文报头。

    bit0~bit3是0011,说明广播类型是SCAN_REQ,即扫描请求。
    bit7(RxAdd)是1:说明广播设备使用的是随机地址。
    bit6(TxAdd)是0:说明扫描设备使用的是公共地址。
        2). 0C:长度,表示SCAN_REQ报文的长度是12个字节。

        3). 7F 0F 72 DD DF 68:扫描设备的公共地址(报头里的TxAdd指示了这个地址是公共地址)。这里使用的实验设备是[艾克姆科技]的EN-nRF51DK开发板和小米3手机,扫描设备是小米3手机,在图3中可以看到该公共地址对应的是Xiao_mico_72。

        4). DA B5 E9 D2 CC F3:广播设备的地址(报头里的RxAdd指示了这个地址是随机地址)。

    3.3.2.3. 校验 

       BD BF 27:24字节CRC校验。

    3.3.3. 捕获SCAN_RSP

      按照《蓝牙4.0BLE抓包(一)》中的描述进行抓包,捕获一个心率计的SCAN_REQ包。

                                                                         图 - 捕获的SCAN_RSP包

    3.3.4. 分析SCAN_RSP

      同样,在这里我们先取出SCAN_REQ包的数据,便于分析。

      D6 BE 89 8E 44 06 DA B5 E9 D2 CC F3 61 6A 0F

    3.3.4.1 接入地址

      D6 BE 89 8E:接入地址,对广播来说是固定值。注意一下这里的字节序,接入地址传输时是低字节在前的。

    3.3.4.2 PDU

        1). 44:广播报文报头。

    bit0~bit3是0100,说明广播类型是SCAN_RSP,即扫描响应。
    bit6(TxAdd)是1:说明广播设备使用的是随机地址。
        2). 06:长度,表示SCAN_ RSP报文的长度是6个字节。

        3). DA B5 E9 D2 CC F3:广播设备的地址(报头里的RxAdd指示了这个地址是随机地址)。

    3.3.4.3 校验 

       61 6A 0F:24字节CRC校验。

    3.4.连接请求CONNECT_REQ

    在低功耗蓝牙技术建立连接的过程中,设备都是成对出现的:master和slave设备。如果master希望与slave建立连接,master就需要发起连接请求(ConnectionRequest,CONNECT_REQ)因此master可以称之为连接发起者;同时,slave必须是可连接的并且具有解析连接请求CONNECT_REQ的能力,slave可以称之为广播者。图1是连接请求CONNECT_REQ的帧结构。


    图1 CONNECT_REQ帧结构

    其中,InitA是连接发起者的蓝牙设备地址,长度为6字节;AdvA是广播者的蓝牙设备地址,长度为6字节。除了InitA和AdvA之外,帧格式中最为重要的部分则是LLData,这一部分包含了在连接建立过程中所需要使用的有意义的参数。

    为了更好的理解连接请求CONNECT_REQ,我们可以在日常生活中找到类似的一个例子,帮助我们理解它的含义。读者们很多应该都有过工作经验,在开始新的工作之前,都需要和雇主签署一份劳动合同,而CONNECT_REQ就是一份由“雇主”master提供的“劳动合同”,只需经过“雇员”slave确认,这份“合同”就开始生效,低功耗蓝牙技术的连接也就建立了。接下来我们就对“合同”中的各项条款逐条进行分析(如图2所示)。


    图2 LLData示意图

    (1)接入地址(AA:Access Address)

    这份合同的第一条款就是为雇员分配一个公司内部的唯一识别码,类似于工号,雇员可以在公司内部使用这一工号;当雇员离开公司之后,唯一识别码自动失效;即使是这一雇员再次加入到这家公司,他/她的新工号也与旧的工号不同。类似的,在两个低功耗蓝牙技术设备建立连接之前,master设备负责生成接入地址,这一地址类似于一个4字节的随机数,当连接建立之后,master和slave都使用这一接入地址进行通信;当连接断开之后,接入地址自动失效。

    (2)CRCInit(CRC初始值)

    这份“合同”的第二条款是CRCInit,它就是雇员在公司内部的一个密钥,通过这个密钥,雇员可以访问公司内部的资源。对于低功耗蓝牙技术设备,master和slave使用CRCIinit来验证数据包的完整性。

    (3)WinSize和WinOffset

    合同的第三条款中规范了雇员首次来公司报到的时间以及今后每次工作的时长。WinSize和WinOffset在低功耗蓝牙技术连接中,也做了类似的定义。WinOffset定义了在CONNECT_REQ命令之后的一个通信窗口的偏移量,如图3所示。在slave设备收到CONNECT_REQ之后,slave设备需要占用一些时间、根据LLData参数进行一些相关的配置,因此,WinOffeset为slave设备进行此种操作提供了时间,transmitWindowOffset= WinOffset×1.25 ms。WinSize定义了设备每次开启收发机的窗口时间,无论是master还是slave,它们都遵循WinSize的定义,窗口时间transmitWindowSize=WinSize×1.25 ms。

    因此,在CONNECT_REQ之后,第一个由master发送到slave的数据帧,我们称之为“锚点”(如图3所示),因为之后的所有的连接事件都以这一时刻为基准,呈现周期性变化。从红色框图中我们可以看到,第一个数据帧的时刻不能早于(1.25ms+transmitWindowOffset),同时也不能晚于(1.25 ms + transmitWindowOffset + transmitWindowSize)。

    图3 发起连接时序图

    (4)Interval, Latency & Timeout

    一般情况下,人们的工作时间是朝九晚五,一周工作五天。但是在低功耗蓝牙技术的连接机制当中,我们采用了更加灵活的“弹性工作制”。对于低功耗蓝牙技术连接的弹性工作制,这里有三个参数需要了解,Interval,Lantency和Timeout。


    图4 连接事件时序图

    在连接建立之后,master和slave之间的数据交互我们可以称之为连接事件,连接事件的发生周期(connInterval)则是由Interval参数来进行设定,connInterval= Interval×1.25 ms, connInterval的取值范围则是在7.5 ms至4 s秒之间。因此,在确定了锚点之后,master和slave将按照connInterval确定的时间间隔进行数据的交互,如图4所示。

    但是,对于低功耗蓝牙技术,低功耗的特性是需要特别考虑的,而且在实际的应用当中,不需要在每次connInterval都产生连接事件,因此引入了参数Lantancy,可以有效的减少连接事件的产生,connSlaveLatency= Latency。connSlaveLatency 定义了slave设备可以忽略多少个连续的连接事件,其不需要在这些被忽略的连接事件中侦听来自master的数据包,这也意味着slave设备不需要在每个连接事件产生的时刻都唤醒并打开射频接收机进行侦听,所以可以有效减少slave设备的功耗。这也是低功耗蓝牙技术能够实现其低功耗特性的一个重要的原因。

    Timeout参数定义了连接超时的长度,connSupervisionTimeout= Timeout×10 ms,其取值范围在100 ms至32 s之间。不论是master还是slave,在其收到一个数据帧之后,如果等待了connSupervisionTimeout时长都没有下一个数据帧到来,则可以认为连接已经断开。在这里要强调的是,connSupervisionTimeout必须大于(1 + connSlaveLatency) × connInterval × 2,否则,slave设备即使是在Lantency状态,也会被误认为是连接超时,导致连接误断开。

    (5)ChM & Hop

    我们都知道,蓝牙使用的是跳频技术,当连接建立之后,master和slave设备就需要利用某种机制来在预先设定的信道图谱上、按照预先设定的跳频跨度进行跳频工作,信道图谱就来自ChM参数,每跳的跨度则来自于Hop参数。Hop是一个整数,取值范围在5至16之间。下面的公式提供了跳频的工作方式:


    ————————————————
    版权声明:本文为CSDN博主「偏执灬」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/sinat_23338865/article/details/52189538

    展开全文
  •  平时我们用蓝牙耳机听音乐,和不同的设备共享文件,打电话等,都有一个配对--连接--传输数据的过程。  蓝牙配对过程,其实就是一个认证的过程。  为什么不配对便无法建立连接?  任何无线通信技术都存在被...
  • android 蓝牙连接与配对

    千次阅读 2018-08-14 13:32:27
    前篇写的蓝牙数据传输,因为当时没要求就没写li连接配对,直接调用的已配对数据。 然后不出所料,测试版给出之后马上反馈回来,说要做连接配对功能,不然去设置里面弄太麻烦了.... 行吧,谁叫我想偷懒叻。然后就做...
  • Android 数据传输方式 WIFI 蓝牙 USB

    千次阅读 2016-12-22 17:30:03
    2、通过UUID来配对蓝牙  1)、安全模式是 要输入配对密码的  2)、非安全模式需要输入配对密码 3、拿到蓝牙类获取BloothSockt进行通信 蓝牙4.0BLE 是针对可穿戴设备推出的低功耗设备。 Andro
  • HC-05蓝牙模块配对方法及AT指令配置

    千次阅读 2019-09-10 16:49:18
    取代传统的数据线,实现无线数据传输,解决距离问题,更加便捷 为什么是HC-05?不是HC-06/08? HC-05蓝牙2.0主从一体的模块,AT指令丰富,功能强大,可操作性强。 HC-06蓝牙2.0主从一体的模块,AT指令简单,使用...
  • 手机利用蓝牙搜索配对蓝牙设备,连接上后蓝牙设备给手机传输数据,此蓝牙设备不是BLE设备,并且通信过程中利用BluetoothSocket进行通信,但是获取到数据,有可能设备传输的数据加密了还是必须知道数据格式?...
  • 在手机蓝牙与基于Arduino扩展出来的蓝牙模块进行连接(这里应该不算是配对)后,开始一段时间内,手机与模块间的通信一切正常,可以完全正确且完整地接收到手机传来的数据,但是在过了一段时间后,知道为何,...
  • Windows10 操作系统新增了蓝牙功能,可以实现对文件资料数据在智能电子设备间进行蓝牙无线传输,需要互联网的支持,操作方法方便快捷,增加了一种智能电子设备之间传输数据的途径。那么怎么通过蓝牙功能把手机上的...
  • Windows10 操作系统新增了蓝牙功能,可以实现对文件资料数据在智能电子设备间进行蓝牙无线传输,需要互联网的支持,操作方法方便快捷,增加了一种智能电子设备之间传输数据的途径。那么怎么通过蓝牙功能把手机上的...
  • Redmi AirDots S真无线蓝牙耳机发布,售价129.9元,新版升级了连接方式,限制主从...官方介绍,Redmi AirDots2配备最新蓝牙5.0芯片,数据传输速率相比上一代蓝牙4.2提升至2倍,连接更快更稳定,听歌、玩游戏更爽...
  • Windows10 操作系统新增了蓝牙功能,可以实现对文件资料数据在智能电子设备间进行蓝牙无线传输,需要互联网的支持,操作方法方便快捷,增加了一种智能电子设备之间传输数据的途径。那么怎么通过蓝牙功能把手机上的...
  • Redmi AirDots S真无线蓝牙耳机发布,售价129.9元,新版升级了连接方式,限制主从...官方介绍,Redmi AirDots2配备最新蓝牙5.0芯片,数据传输速率相比上一代蓝牙4.2提升至2倍,连接更快更稳定,听歌、玩游戏更爽...
  • 想要“偷走”朋友手机里的一部好电影,但手机没流量了;上班没带数据线,做好的PPT演示...方法一:蓝牙传输操作步骤(双方):打开蓝牙 —— 蓝牙配对 —— 选择传输资源 —— 发送蓝牙是最传统的点对点传输解决方案...
  • 你是不是遇到过这样的情况:朋友聚会的时候,手机中有好的游戏、音乐或者电影想要和朋友分享,在手边没有电脑没有数据线时你首先想到的应该就是蓝牙,但是先抛开不同设备间的蓝牙配对麻烦说,靠蓝牙传输差不多100...
  • 你是不是遇到过这样的情况:朋友聚会的时候,手机中有好的游戏、音乐或者电影想要和朋友分享,在手边没有电脑没有数据线时你首先想到的应该就是蓝牙,但是先抛开不同设备间的蓝牙配对麻烦说,靠蓝牙传输差不多100...

空空如也

空空如也

1 2 3 4
收藏数 78
精华内容 31
热门标签
关键字:

蓝牙不配对传输数据