İki cihaz arasında bağlantı oluşturmak için hem sunucu tarafı hem de istemci tarafı mekanizmalarını uygulamanız gerekir. Çünkü bir cihazın sunucu yuvasını açması ve diğerinin sunucu cihazının MAC adresini kullanarak bağlantıyı başlatması gerekir. Sunucu cihazı ve istemci cihaz, gerekli BluetoothSocket
'i farklı şekillerde edinir. Gelen bağlantı kabul edildiğinde sunucu yuva bilgilerini alır. İstemci, sunucuya bir RFCOMM kanalı açtığında yuva bilgilerini sağlar.
Sunucu ve istemcinin her biri aynı RFCOMM kanalında bağlı bir BluetoothSocket
'e sahip olduğunda birbirine bağlı olarak kabul edilir. Bu noktada, her cihaz giriş ve çıkış akışlarını alabilir ve veri aktarımı başlayabilir. Bu konu, Bluetooth verilerini aktarma konulu bölümde ele alınmaktadır. Bu bölümde, iki cihaz arasında bağlantının nasıl başlatılacağı açıklanmaktadır.
Bluetooth cihazları bulmaya çalışmadan önce uygun Bluetooth izinlerine sahip olduğunuzdan ve uygulamanızı Bluetooth için ayarladığınızdan emin olun.
Bağlantı teknikleri
Uygulama tekniklerinden biri, her cihazı otomatik olarak sunucu şeklinde hazırlamaktır. Böylece her cihazda açık bir sunucu yuvası olur ve bağlantıları dinleyebilirsiniz. Bu durumda, cihazlardan biri diğeriyle bağlantı başlatabilir ve istemci haline gelebilir. Alternatif olarak, bir cihaz bağlantıyı açıkça barındırıp istek üzerine bir sunucu yuvası açabilir ve diğer cihaz bağlantıyı başlatır.
Şekil 1. Bluetooth eşleme iletişim kutusu.
Sunucu olarak bağlan
İki cihazı birbirine bağlamak istediğinizde, cihazlardan biri açık BluetoothServerSocket
tutarak sunucu
görevini yapmalıdır.
Sunucu yuvasının amacı, gelen bağlantı isteklerini dinlemek ve istek kabul edildikten sonra bağlı bir BluetoothSocket
sağlamaktır. BluetoothSocket
, BluetoothServerSocket
kaynağından edinildiğinde, cihazın daha fazla bağlantı kabul etmesini istemiyorsanız BluetoothServerSocket
silinebilir ve silinmelidir.
Bir sunucu yuvası kurmak ve bağlantıyı kabul etmek için aşağıdaki adımları uygulayın:
listenUsingRfcommWithServiceRecord(String, UUID)
numaralı telefonu arayarakBluetoothServerSocket
edinin.Dize, hizmetinizin tanımlayıcı bir adıdır. Bu ad, sistem tarafından otomatik olarak cihazdaki yeni bir Hizmet Keşif Protokolü (SDP) veritabanı girişine yazılır. Bu ad rastgeledir ve uygulamanızın adı olabilir. Evrensel Benzersiz Tanımlayıcı (UUID) da SDP girişine dahil edilir ve istemci cihazla bağlantı sözleşmesinin temelini oluşturur. Yani istemci bu cihaza bağlanmaya çalıştığında, bağlanmak istediği hizmeti benzersiz şekilde tanımlayan bir UUID taşır. Bağlantının kabul edilmesi için bu UUID'ler eşleşmelidir.
UUID, bilgileri benzersiz bir şekilde tanımlamak için kullanılan bir dize kimliği için standartlaştırılmış 128 bitlik bir biçimdir. UUID'nin tekrarlanma olasılığı neredeyse sıfır olduğu için bir sistem veya ağ içinde benzersiz olması gereken bilgileri tanımlamak için kullanılır. Merkezi bir otoriteye ihtiyaç duyulmadan, bağımsız olarak oluşturulur. Bu durumda, uygulamanızın Bluetooth hizmetini benzersiz bir şekilde tanımlamak için kullanılır. Uygulamanızla kullanmak üzere bir UUID almak için web'deki çok sayıda rastgele
UUID
oluşturucudan birini kullanabilir, ardındanfromString(String)
ile bir UUID başlatabilirsiniz.accept()
numaralı telefonu arayarak bağlantı isteklerini dinlemeye başlayın.Bu engelleme amaçlı bir arama. Bir bağlantı kabul edildiğinde veya bir istisna oluştuğunda döndürülür. Bağlantı, yalnızca uzak cihaz bu dinleme sunucusu soketine kayıtlı olan UUID ile eşleşen bir bağlantı isteği gönderdiğinde kabul edilir. Başarılı olduğunda
accept()
, bağlı birBluetoothSocket
döndürür.Ek bağlantıları kabul etmek istemiyorsanız
close()
numaralı telefonu arayın.Bu yöntem çağrısı, sunucu yuvasını ve tüm kaynaklarını serbest bırakır ancak
accept()
tarafından döndürülen bağlıBluetoothSocket
öğesini kapatmaz. TCP/IP'nin aksine RFCOMM, aynı anda kanal başına yalnızca bir bağlı istemciye izin verir. Bu nedenle çoğu durumda, bağlı bir yuvayı kabul ettikten hemen sonraBluetoothServerSocket
üzerindeclose()
çağrısı yapmak mantıklıdır.
accept()
çağrısı bir engelleme çağrısı olduğundan, bunu ana etkinlik kullanıcı arayüzü iş parçacığında yürütmeyin. Kodu başka bir iş parçacığında yürütmek, uygulamanızın diğer kullanıcı etkileşimlerine hâlâ yanıt verebilmesini sağlar. BluetoothServerSocket
veya BluetoothSocket
içeren tüm işleri uygulamanız tarafından yönetilen yeni bir iş parçacığında yapmak genellikle mantıklıdır. accept()
gibi engellenmiş bir çağrıyı iptal etmek için başka bir iş parçacığından BluetoothServerSocket
veya BluetoothSocket
üzerinde close()
çağrısı yapın. BluetoothServerSocket
veya BluetoothSocket
üzerindeki tüm yöntemlerin iş parçacığı güvenli olduğunu unutmayın.
Örnek
Aşağıda, gelen bağlantıları kabul eden sunucu bileşeni için basitleştirilmiş bir iş parçacığı verilmiştir:
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); } } }
Bu örnekte, yalnızca bir gelen bağlantı istenir. Bu nedenle bir bağlantı kabul edilip BluetoothSocket
edinildiğinde, uygulama alınan BluetoothSocket
öğesini ayrı bir iş parçacığına iletir, BluetoothServerSocket
öğesini kapatır ve döngüden çıkar.
accept()
, BluetoothSocket
değerini döndürdüğünde yuvanın zaten bağlı olduğunu unutmayın. Bu nedenle, istemci tarafında yaptığınız gibi connect()
çağrısı yapmamalısınız.
Uygulamaya özel manageMyConnectedSocket()
yöntemi, veri aktarımına yönelik ileti dizisini başlatmak için tasarlanmıştır. Bu yöntem, Bluetooth verilerini aktarma konulu başlıkta ele alınmaktadır.
Genellikle gelen bağlantıları dinlemeyi bitirir bitirmez BluetoothServerSocket
cihazınızı kapatmanız gerekir. Bu örnekte, close()
BluetoothSocket
edinilir edinmez çağrılır. Sunucu soketinde dinlemeyi bırakmanız gerekirse iş parçacığınızda özel BluetoothSocket
öğesini kapatabilecek bir herkese açık yöntem de sağlayabilirsiniz.
İstemci olarak bağlanın
Açık sunucu yuvasında bağlantıları kabul eden uzak bir cihazla bağlantı başlatmak için önce uzak cihazı temsil eden bir BluetoothDevice
nesnesi edinmeniz gerekir. Nasıl BluetoothDevice
oluşturacağınızı öğrenmek için Bluetooth cihazları bulma bölümüne bakın. Ardından bir BluetoothSocket
edinmek ve bağlantıyı başlatmak için BluetoothDevice
kullanmanız gerekir.
Temel prosedür aşağıdaki gibidir:
BluetoothDevice
ilecreateRfcommSocketToServiceRecord(UUID)
numaralı telefonu arayarakBluetoothSocket
kazanın.Bu yöntem, istemcinin bir
BluetoothDevice
öğesine bağlanmasına olanak tanıyan birBluetoothSocket
nesnesini başlatır. Buraya iletilen UUID,BluetoothServerSocket
açmak içinlistenUsingRfcommWithServiceRecord(String, UUID)
çağrılırken sunucu cihazı tarafından kullanılan UUID ile eşleşmelidir. Eşleşen bir UUID kullanmak için UUID dizesini uygulamanıza sabit bir şekilde kodlayın ve ardından hem sunucu hem de istemci kodundan bu dizeye referans verin.connect()
yöntemini çağırarak bağlantıyı başlatın. Bu yöntemin engelleme çağrısı olduğunu unutmayın.Bir istemci bu yöntemi çağırdıktan sonra sistem, eşleşen UUID'ye sahip uzak cihazı bulmak için bir SDP araması gerçekleştirir. Arama başarılı olursa ve uzak cihaz bağlantıyı kabul ederse bağlantı sırasında kullanmak üzere RFCOMM kanalını paylaşır ve
connect()
yöntemi geri döner. Bağlantı başarısız olursa veyaconnect()
yöntemi zaman aşımına uğrarsa (yaklaşık 12 saniye sonra) yöntem birIOException
gönderir.
connect()
engelleme çağrısı olduğundan bu bağlantı prosedürünü her zaman ana etkinlik (kullanıcı arayüzü) iş parçacığından ayrı bir iş parçacığında gerçekleştirmeniz gerekir.
Örnek
Aşağıda, Bluetooth bağlantısı başlatan bir istemci iş parçacığına temel bir örnek verilmiştir:
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); } } }
Bu snippet'te cancelDiscovery()
, bağlantı girişimi gerçekleşmeden önce çağrılır. Özellikle cihaz keşfinin o anda devam edip etmediğinden bağımsız olarak cancelDiscovery()
başarılı olduğundan connect()
öncesinde her zaman cancelDiscovery()
çağırmanız gerekir. Uygulamanızın, cihaz keşfinin devam edip etmediğini belirlemesi gerekiyorsa isDiscovering()
kullanarak kontrol edebilirsiniz.
Uygulamaya özel manageMyConnectedSocket()
yöntemi, Bluetooth verilerinin aktarılması ile ilgili bölümde ele alınan veri aktarımı iş parçacığını başlatmak için tasarlanmıştır.
BluetoothSocket
ile işiniz bittiğinde her zaman close()
numaralı telefonu arayın. Bu işlem, bağlı yuvayı hemen kapatır ve ilgili tüm dahili kaynakları serbest bırakır.