1

我正在基于众所周知的 BluetoothChat 示例开发支持蓝牙的应用程序。基本上,使用这个应用程序,客户端可以将一些数据包发送到服务器。

我已经使用两部 Xperia 智能手机(Xperia X8 和 Xperia Sola,android 2.1 和 4.0)测试了该应用程序,一切正常:它们都可以充当客户端或服务器。

不幸的是,如果我使用 HTC Desire (android 2.3) 作为服务器,它将无法接受来自 Xperia 客户端之一的传入连接。似乎客户端connect()返回好像一切都很好,但服务器却被阻止了accept(),好像什么都没发生一样。

相关代码片段:

1.“接受线程”

private class BluetoothAcceptThread extends Thread
    {
        private final BluetoothServerSocket serverSocket;

        public BluetoothAcceptThread()
        {
            BluetoothServerSocket tmpSocket = null;      

            try   
            {
                Method m = bluetoothAdapter.getClass().getMethod("listenUsingRfcommOn", new Class[] {int.class});
                tmpSocket = (BluetoothServerSocket) m.invoke(bluetoothAdapter, APP_BT_CHANNEL);
            }
            catch (NoSuchMethodException e)
            {
                e.printStackTrace();
            }
            catch (InvocationTargetException e)
            {
                Log.e(MainActivity.ERROR_TAG, "BluetoothAcceptThread listen() (with reflection) failed", e);
            }
            catch (IllegalAccessException e)
            {
                e.printStackTrace();
            }

            serverSocket = tmpSocket;
            Log.d(MainActivity.DEBUG_TAG, "BluetoothAcceptThread ServerSocket created");
        }

        @Override
        public void run()
        {
            BluetoothSocket socket = null;

            try
            {
                Log.d(MainActivity.DEBUG_TAG, "BluetoothAcceptThread calling accept()...");
                socket = serverSocket.accept();
                Log.d(MainActivity.DEBUG_TAG, "BluetoothAcceptThread accept() returned");
            }
            catch (IOException e)
            {
                Log.e(MainActivity.ERROR_TAG, "BluetoothAcceptThread accept() failed: " + e.getMessage());
            }

            if (socket != null)
            {
                Log.d(MainActivity.DEBUG_TAG, "BluetoothAcceptThread accept() successfully");

                synchronized (BluetoothManager.this)
                {
                    if (currentState == SocketState.LISTENING || currentState == SocketState.CONNECTING)
                        startBluetoothConnection(socket);  // all is ok, it can proceed

                    else if (currentState == SocketState.INACTIVE || currentState == SocketState.CONNECTED)
                        cancel(socket);     
                }
            }
        }

        @Override
        public void cancel()
        {
            try
            {
                serverSocket.close();
                Log.d(MainActivity.DEBUG_TAG, "BluetoothAcceptThread ServerSocket closed");
            }
            catch (IOException e)
            {
                Log.e(MainActivity.ERROR_TAG, "BluetoothAcceptThread close() failed", e);
            }
        }

        private void cancel(BluetoothSocket newSocket)
        {
            try
            {
                newSocket.close();
                Log.d(MainActivity.DEBUG_TAG, "BluetoothAcceptThread client socket closed");
            }
            catch (IOException e)
            {
                Log.e(MainActivity.ERROR_TAG, "BluetoothAcceptThread client socket close() failed", e);
            }
        }
    }

2.“连接线程”

private class BluetoothConnectThread extends Thread
    {
        private final BluetoothSocket socket;
        private final BluetoothDevice device;

        public BluetoothConnectThread(BluetoothDevice d)
        {
            device = d;
            BluetoothSocket tmpSocket = null;  

            try   
            {
                Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
                tmpSocket = (BluetoothSocket) m.invoke(device, APP_BT_CHANNEL);
            }
            catch (NoSuchMethodException e)
            {
                e.printStackTrace();
            }
            catch (InvocationTargetException e)
            {
                Log.e(MainActivity.ERROR_TAG, "BluetoothConnectThread create() (with reflection) failed", e);
            }
            catch (IllegalAccessException e)
            {
                e.printStackTrace();
            }

            socket = tmpSocket;
            Log.d(MainActivity.DEBUG_TAG, "BluetoothConnectThread client socket created");
        }

        @Override
        public void run()
        {
            stopBluetoothDiscovery();  // otherwise it will slow down the connection

            try
            {
                socket.connect();
                Log.d(MainActivity.DEBUG_TAG, "BluetoothConnectThread connect() successfully");
            }
            catch (IOException e)
            {
                Log.e(MainActivity.ERROR_TAG, "BluetoothConnectThread connect() failed", e);
                String deviceName = device != null ? device.getName() : "none";
                connectionFailed(deviceName);  // notify UI thread
                return;
            }

            synchronized (BluetoothManager.this)
            {
                bluetoothConnectThread = null;   
            }

            startBluetoothConnection(socket);  // create the "Communication" Thread
        }

        @Override
        public void cancel()
        {
            try
            {
                socket.close();
                Log.d(MainActivity.DEBUG_TAG, "BluetoothConnectThread client socket closed");
            }
            catch (IOException e)
            {
                Log.e(MainActivity.ERROR_TAG, "BluetoothConnectThread close() failed", e);
            }
        }
    }

3.“通信线程”(BluetoothChat 示例中的 ConnectedThread)

private class BluetoothCommunicationThread extends Thread
    {
        private final BluetoothSocket socket;
        private final InputStream inputStream;
        private final OutputStream outputStream;

        public BluetoothCommunicationThread(BluetoothSocket s)
        {
            socket = s;
            InputStream in = null;
            OutputStream out = null;

            try
            {
                in = socket.getInputStream();
                out = socket.getOutputStream();
            }
            catch (IOException e)
            {
                Log.e(MainActivity.ERROR_TAG, "BluetoothCommunicationThread failed to get streams", e);
            }

            inputStream = in;
            outputStream = out;
        }

        @Override
        public void run()
        {
            byte[] buffer = new byte[BT_BUFF_SIZE];
            int readBytes;    

            while (true)
            {
                try
                {
                    readBytes = inputStream.read(buffer, 0, buffer.length);

                    if (readBytes != -1)
                    {
                        Message message = messageHandler.obtainMessage(DATA_MSG, readBytes, -1, buffer);
                        message.sendToTarget();  // notify to UI thread the bytes counter
                    }
                    else
                    {
                        BluetoothDevice device = socket.getRemoteDevice();
                        String deviceName = device != null ? device.getName() : "none";
                        connectionLost(deviceName);
                        break;
                    }
                }
                catch (IOException e)
                {
                    Log.e(MainActivity.ERROR_TAG, "BluetoothCommunicationThread read() failed", e);
                    BluetoothDevice device = socket.getRemoteDevice();
                    String deviceName = device != null ? device.getName() : "none";
                    connectionLost(deviceName);
                    break;
                }
            }
        }

        public void write(byte[] buffer)
        {
            try
            {
                outputStream.write(buffer);
            }
            catch (IOException e)
            {
                Log.e(MainActivity.ERROR_TAG, "BluetoothCommunicationThread write() failed", e);
            }
        }

        @Override
        public void cancel()
        {
            try
            {
                socket.close();
                Log.d(MainActivity.DEBUG_TAG, "BluetoothCommunicationThread socket closed");
            }
            catch (IOException e)
            {
                Log.e(MainActivity.ERROR_TAG, "BluetoothCommunicationThread close() failed", e);
            }
        }
    }

所以问题的步骤如下:

  1. HTC Desire 服务器调用accept()
  2. Xperia 客户端调用connect()
  3. connect返回好像连接已建立
  4. HTC 什么都没有发生,总是被屏蔽accept()
  5. Xperia 客户端认为它已连接,因此它创建了 CommunicationThread 并调用了阻塞read();此函数抛出java.io.IOException: Software caused connection abort,可能是因为套接字未连接。

最后这些是相关的日志:

Xperia 客户端:

09-20 00:44:23.562    9106-9106/com.powertester D/[PowerTester Debug]﹕ BluetoothConnectThread client socket created
09-20 00:44:25.704    9106-9579/com.powertester D/[PowerTester Debug]﹕ BluetoothConnectThread connect() successfully
09-20 00:44:25.734    9106-9579/com.powertester D/[PowerTester Debug]﹕ BluetoothCommunicationThread started and I/O streams ready
09-20 00:44:25.764    9106-9589/com.powertester E/[PowerTester Error]﹕ BluetoothCommunicationThread read() failed
        java.io.IOException: Software caused connection abort
        at android.bluetooth.BluetoothSocket.readNative(Native Method)
        at android.bluetooth.BluetoothSocket.read(BluetoothSocket.java:333)
        at android.bluetooth.BluetoothInputStream.read(BluetoothInputStream.java:96)
        at com.powertester.net.BluetoothManager$BluetoothCommunicationThread.run(BluetoothManager.java:518)
09-20 00:44:25.844    9106-9106/com.powertester D/[PowerTester Debug]﹕ BluetoothCommunicationThread socket closed

宏达服务器

09-19 15:47:07.591    2422-2422/com.powertester D/[PowerTester Debug]﹕ BluetoothAcceptThread ServerSocket created
09-19 15:47:07.591    2422-2484/com.powertester D/[PowerTester Debug]﹕ BluetoothAcceptThread calling accept()...

真正奇怪的是,如果将 HTC Desire用作客户端,而将其中一台 Xperia 用作服务器,它就可以工作。

那么,是我的应用程序有问题还是 HTC Desire 蓝牙堆栈有问题?

4

1 回答 1

1

经过一些麻烦后,我意识到问题在于反射本身和蓝牙通道的显式使用。

使用正常方式(即未隐藏的蓝牙方法)我的应用程序运行良好。

于 2013-10-03T11:02:10.433 回答