ربط أجهزة تتضمّن بلوتوث

لإنشاء اتصال بين جهازين، يجب تنفيذ كل من آليات من جانب الخادم ومن جانب العميل لأنه يجب على أحد الأجهزة فتح خادم ويبدأ الاتصال من خلال جهاز الخادم عنوان MAC. يحصل كل من جهاز الخادم وجهاز العميل على البيانات BluetoothSocket بعبارات مختلفة وطرق. يتلقى الخادم معلومات المقبس عندما يكون اتصال وارد مقبول. يوفر العميل معلومات المقبس عند فتح قناة RFCOMM إلى الخادم.

ويتم اعتبار الخادم والعميل متصلَين ببعضهما البعض عندما يكون لكل منهما BluetoothSocket مرتبط على قناة RFCOMM نفسها. في هذه المرحلة، كان كل جهازك الحصول على مصادر إدخال وإخراج، كما يمكن أن يبدأ نقل البيانات، سنناقشها في القسم حول نقل بيانات البلوتوث. . هذا القسم يصف كيفية بدء الاتصال بين جهازين.

تأكد من أن لديك الأدوات المناسبة أذونات البلوتوث عليك إعداد تطبيقك لاستخدام البلوتوث قبل. محاولة العثور على أجهزة تتضمّن بلوتوث.

تقنيات الاتصال

ويتمثل أحد أساليب التنفيذ في إعداد كل جهاز تلقائيًا كخادم بحيث يكون لكل جهاز مقبس خادم مفتوح ويتلقى الاتصالات. ضِمن في هذه الحالة، يمكن لأي من الجهازين بدء الاتصال بالآخر البرنامج. وبدلاً من ذلك، يمكن لجهاز واحد استضافة الاتصال بشكل صريح وفتح مقبس الخادم عند الطلب، ويبدأ الجهاز الآخر الاتصال.


الشكل 1. مربّع حوار إقران البلوتوث

الاتصال كخادم

عندما تريد توصيل جهازين، يجب أن يعمل أحدهما كخادم عن طريق الضغط على أمر open (فتح) BluetoothServerSocket الغرض من مقبس الخادم هو الاستماع إلى طلبات الاتصال الواردة وتقديم BluetoothSocket مرتبط بعد قبول الطلب. عندما تم الحصول على BluetoothSocket من BluetoothServerSocket، يمكن ويجب تجاهل BluetoothServerSocket، ما لم تكن تريد ذلك. الجهاز لقبول المزيد من الاتصالات.

لإعداد مقبس الخادم وقبول الاتصال، أكمل ما يلي تسلسل الخطوات:

  1. الحصول على BluetoothServerSocket من خلال الاتصال listenUsingRfcommWithServiceRecord(String, UUID)

    السلسلة هي اسم يعرّف عن خدمتك، ويستجيب النظام يكتب تلقائيًا إلى إدخال جديد في قاعدة بيانات بروتوكول اكتشاف الخدمة (SDP) على الجهاز. ويكون هذا الاسم عشوائيًا ويمكن أن يكون ببساطة اسم تطبيقك. يتم تضمين المعرّف الفريد العالمي (UUID) أيضًا في إدخال SDP وتشكل الأساس لاتفاقية الاتصال مع جهاز العميل. الذي عندما يحاول العميل الاتصال بهذا الجهاز، يحمل معرّفًا فريدًا (UUID) يعرِّف بشكلٍ فريد الخدمة التي يريد الاتصال بها. هذه يجب أن تتطابق أرقام التعريف الفريدة العالمية (UUID) ليتم قبول الاتصال.

    المعرّف الفريد العالمي (UUID) هو تنسيق موحّد مكوّن من 128 بت لمعرّف سلسلة يُستخدم بشكل فريد معلومات الهوية. يتم استخدام المعرّف الفريد العالمي (UUID) لتحديد المعلومات التي يجب فريدة داخل النظام أو الشبكة لأن احتمالية وجود معرّف فريد عالمي (UUID) والمكرر هو صفر بشكل فعال. ويتم إنشاؤها بشكل مستقل، بدون استخدام للسلطة المركزية. وفي هذه الحالة، يتم استخدامه لتعريف هوية موقعك الإلكتروني خدمة البلوتوث في التطبيق. للحصول على معرّف فريد عالمي (UUID) لاستخدامه مع تطبيقك، يمكنك استخدام أحدها من النتائج العشوائية العديدة UUID على الويب، ثم إعداد معرّف فريد عالمي (UUID) مع fromString(String)

  2. ابدأ الاستماع إلى طلبات الاتصال عن طريق الاتصال accept()

    هذه مكالمة حظر. يعود عندما يتم إجراء أي اتصال قبولها أو حدوث استثناء. لا يتم قبول الاتصال إلا عندما أرسل الجهاز البعيد طلب اتصال يحتوي على معرّف فريد عالمي (UUID) يطابق الذي تم تسجيله في مقبس خادم الاستماع هذا. عند نجاحها، تعرض الدالة accept() جهاز BluetoothSocket متصلاً.

  3. إذا لم تكن تريد قبول أي اتصالات إضافية، يمكنك الاتصال close()

    يقوم استدعاء هذه الطريقة بتحرير مقبس الخادم وجميع موارده، ولكن لا يغلق BluetoothSocket المتصل الذي تم إرجاعه من خلال accept() بخلاف بروتوكول TCP/IP، يسمح RFCOMM باستخدام عميل متصل واحد فقط لكل في كل مرة، لذا من المنطقي في معظم الحالات الاتصال بـ close() BluetoothServerSocket فور قبول مقبس متّصل

بما أنّ استدعاء accept() هو استدعاء حظر، لا يتم تنفيذه في واجهة برمجة التطبيقات الرئيسية. مؤشر واجهة مستخدم النشاط ويؤدي تنفيذه في سلسلة محادثات أخرى إلى ضمان لا تزال تستجيب لتفاعلات المستخدم الأخرى. من المنطقي عادةً القيام بكل الأعمال تتضمّن BluetoothServerSocket أو BluetoothSocket في سلسلة محادثات جديدة يديره تطبيقك. لإلغاء مكالمة محظورة مثل accept()، اتصل بالرقم close() على BluetoothServerSocket أو BluetoothSocket من سلسلة محادثات أخرى. ملاحظة أنّ جميع الطرق في BluetoothServerSocket أو BluetoothSocket هي أن تكون آمنة في سلاسل المحادثات.

مثال

فيما يلي سلسلة محادثات مبسطة لمكوِّن الخادم الذي يقبل الاتصالات الواردة:

Kotlin

private inner class AcceptThread : Thread() {

   private val mmServerSocket: BluetoothServerSocket? by lazy(LazyThreadSafetyMode.NONE) {
       bluetoothAdapter?.listenUsingInsecureRfcommWithServiceRecord(NAME, MY_UUID)
   }

   override fun run() {
       // Keep listening until exception occurs or a socket is returned.
       var shouldLoop = true
       while (shouldLoop) {
           val socket: BluetoothSocket? = try {
               mmServerSocket?.accept()
           } catch (e: IOException) {
               Log.e(TAG, "Socket's accept() method failed", e)
               shouldLoop = false
               null
           }
           socket?.also {
               manageMyConnectedSocket(it)
               mmServerSocket?.close()
               shouldLoop = false
           }
       }
   }

   // Closes the connect socket and causes the thread to finish.
   fun cancel() {
       try {
           mmServerSocket?.close()
       } catch (e: IOException) {
           Log.e(TAG, "Could not close the connect socket", e)
       }
   }
}

Java

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 = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
       } catch (IOException e) {
           Log.e(TAG, "Socket's listen() method failed", 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) {
               Log.e(TAG, "Socket's accept() method failed", e);
               break;
           }

           if (socket != null) {
               // A connection was accepted. Perform work associated with
               // the connection in a separate thread.
               manageMyConnectedSocket(socket);
               mmServerSocket.close();
               break;
           }
       }
   }

   // Closes the connect socket and causes the thread to finish.
   public void cancel() {
       try {
           mmServerSocket.close();
       } catch (IOException e) {
           Log.e(TAG, "Could not close the connect socket", e);
       }
   }
}

في هذا المثال، لا يلزم سوى اتصال وارد واحد، فبمجرد أن تم قبول اتصال واكتساب BluetoothSocket، يجتاز التطبيق تم الحصول على BluetoothSocket في سلسلة محادثات منفصلة، يؤدي إلى إغلاق BluetoothServerSocket، والخروج من التكرار الحلقي.

يُرجى العِلم أنّه عندما يعرض accept() القيمة BluetoothSocket، يكون المقبس متوفّرًا. متصلين. لذلك، لا يجب عليك استدعاء connect()، كما تفعل من جانب العميل.

تم تصميم طريقة manageMyConnectedSocket() الخاصة بالتطبيق لبدء لنقل البيانات، والتي تمت مناقشتها في موضوع حول نقل البلوتوث

عليك عادةً إغلاق "BluetoothServerSocket" فور الانتهاء والاستماع إلى الاتصالات الواردة. في هذا المثال، يتم استدعاء close() في أقرب وقت نظرًا للاستحواذ على BluetoothSocket. قد ترغب أيضًا في تقديم صورة طريقة في سلسلة المحادثات يمكن أن تغلق BluetoothSocket الخاصة في الحدث أنك تحتاج إلى التوقف عن الاستماع على مقبس الخادم هذا.

التواصل كعميل

لبدء اتصال بجهاز بعيد يقبل اتصالات على مقبس خادم مفتوح، يجب أولاً الحصول على BluetoothDevice يمثل الجهاز البعيد. لمعرفة كيفية إنشاء BluetoothDevice، راجع العثور على بلوتوث الأجهزة. يجب بعد ذلك، يمكن استخدام BluetoothDevice للحصول على BluetoothSocket وبدء الاتصال.

يكون الإجراء الأساسي كما يلي:

  1. باستخدام BluetoothDevice، يمكنك الحصول على BluetoothSocket من خلال الاتصال. createRfcommSocketToServiceRecord(UUID)

    تؤدي هذه الطريقة إلى إعداد كائن BluetoothSocket يسمح للعميل بما يلي: الاتصال بجهاز BluetoothDevice يجب أن يتطابق المعرّف الفريد العالمي (UUID) الذي تم إدخاله هنا مع المعرّف الفريد العالمي (UUID) المستخدَم. بواسطة جهاز الخادم عند استدعائها listenUsingRfcommWithServiceRecord(String, UUID) لفتح BluetoothServerSocket. لاستخدام معرّف فريد عالمي مطابق، عليك ترميز إدخال سلسلة UUID في تطبيقك، ثم الإشارة إليها من الخادم ورمز العميل.

  2. يمكنك بدء عملية الربط من خلال الاتصال بـ connect(). لاحظ أن هذه الطريقة مكالمة حظر.

    بعد استدعاء البرنامج لهذه الطريقة، يُجري النظام بحث SDP للعثور على الجهاز البعيد مع المعرّف الفريد العالمي (UUID) المتطابق. إذا تم البحث ناجحًا وتم أن الجهاز البعيد يقبل الاتصال، فهو يشارك قناة RFCOMM لاستخدام أثناء عملية الربط، ويتم إرجاع طريقة connect(). إذا كان الاتصال إذا فشلت، أو إذا انتهت مهلة طريقة connect() (بعد 12 ثانية تقريبًا)، يمكن عندها تطرح الطريقة IOException.

بما أنّ "connect()" عبارة عن طلب حظر، عليك تنفيذ هذا الإجراء في أي وقت. إجراء الاتصال في سلسلة محادثات منفصلة عن النشاط الرئيسي (UI) .

مثال

فيما يلي مثال أساسي لسلسلة محادثات العميل التي تبدأ عملية بلوتوث الاتصال:

Kotlin

private inner class ConnectThread(device: BluetoothDevice) : Thread() {

   private val mmSocket: BluetoothSocket? by lazy(LazyThreadSafetyMode.NONE) {
       device.createRfcommSocketToServiceRecord(MY_UUID)
   }

   public override fun run() {
       // Cancel discovery because it otherwise slows down the connection.
       bluetoothAdapter?.cancelDiscovery()

       mmSocket?.let { socket ->
           // Connect to the remote device through the socket. This call blocks
           // until it succeeds or throws an exception.
           socket.connect()

           // The connection attempt succeeded. Perform work associated with
           // the connection in a separate thread.
           manageMyConnectedSocket(socket)
       }
   }

   // Closes the client socket and causes the thread to finish.
   fun cancel() {
       try {
           mmSocket?.close()
       } catch (e: IOException) {
           Log.e(TAG, "Could not close the client socket", e)
       }
   }
}

Java

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;

       try {
           // Get a BluetoothSocket to connect with the given BluetoothDevice.
           // MY_UUID is the app's UUID string, also used in the server code.
           tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
       } catch (IOException e) {
           Log.e(TAG, "Socket's create() method failed", e);
       }
       mmSocket = tmp;
   }

   public void run() {
       // Cancel discovery because it otherwise slows down the connection.
       bluetoothAdapter.cancelDiscovery();

       try {
           // Connect to the remote device through the socket. This call blocks
           // until it succeeds or throws an exception.
           mmSocket.connect();
       } catch (IOException connectException) {
           // Unable to connect; close the socket and return.
           try {
               mmSocket.close();
           } catch (IOException closeException) {
               Log.e(TAG, "Could not close the client socket", closeException);
           }
           return;
       }

       // The connection attempt succeeded. Perform work associated with
       // the connection in a separate thread.
       manageMyConnectedSocket(mmSocket);
   }

   // Closes the client socket and causes the thread to finish.
   public void cancel() {
       try {
           mmSocket.close();
       } catch (IOException e) {
           Log.e(TAG, "Could not close the client socket", e);
       }
   }
}

لاحظ أنه في هذا المقتطف، يتم استدعاء cancelDiscovery() قبل الربط محاولة الوصول. عليك الاتصال بـ cancelDiscovery() دائمًا قبل connect()، خاصةً أنّ cancelDiscovery() تنجح بغض النظر عما إذا كان الجهاز عملية الاكتشاف قيد التقدم حاليًا. إذا كان تطبيقك بحاجة إلى تحديد ما إذا كان اكتشاف الجهاز قيد التقدم، يمكنك التحقق باستخدام isDiscovering()

تم تصميم طريقة manageMyConnectedSocket() الخاصة بالتطبيق لبدء موضوع لنقل البيانات، وهو ما ستتم مناقشته في القسم الذي يتناول نقل بيانات البلوتوث.

عند الانتهاء من استخدام "BluetoothSocket"، يُرجى الاتصال دائمًا بالرقم close(). إجراء ذلك إغلاق المقبس المتصل على الفور وإطلاق جميع البيانات الداخلية ذات الصلة الموارد.