Aby utworzyć połączenie między 2 urządzeniami, musisz zaimplementować oba
po stronie serwera i klienta, ponieważ jedno urządzenie musi otworzyć serwer
i drugie musi zainicjować połączenie przy użyciu
Adres MAC. Serwer i urządzenie klienckie uzyskują wymagane
BluetoothSocket
w różnych
kilka sposobów. Serwer odbiera informacje o gniazdach, gdy połączenie przychodzące jest
zaakceptowano. Klient podaje informacje o gniazdku, gdy otwiera kanał RFCOMM
do serwera.
Serwer i klient są uważane za połączone, jeśli mają
połączone urządzenie BluetoothSocket
w tym samym kanale RFCOMM. W tym momencie każdy
może pobierać strumienie wejściowe i wyjściowe i może rozpocząć się przesyłanie danych,
jest omówione w sekcji o przenoszeniu Bluetootha,
danych. Ta sekcja
zawiera opis sposobu inicjowania połączenia między dwoma urządzeniami.
Upewnij się, że masz odpowiedni Uprawnienia Bluetooth oraz skonfiguruj Bluetooth w aplikacji przed wyszukiwania urządzeń Bluetooth.
Techniki nawiązywania połączeń
Jedną z technik implementacji jest automatyczne przygotowanie każdego urządzenia jako serwera dzięki czemu każde urządzenie ma otwarte gniazdo serwera i nasłuchuje połączeń. W w tym przypadku jedno urządzenie może zainicjować połączenie z drugim i stać się klienta. Jedno urządzenie może też bezpośrednio hostować połączenie i otworzyć plik z gniazda serwera na żądanie, a drugie urządzenie inicjuje połączenie.
()
Rysunek 1. Okno parowania Bluetooth.
Połącz jako serwer
Jeśli chcesz połączyć 2 urządzenia, jedno musi działać jako serwer, przytrzymując przycisk
otwórz
BluetoothServerSocket
Gniazdo serwera służy do nasłuchiwania przychodzących żądań połączenia
i po zaakceptowaniu prośby podaj BluetoothSocket
połączony. Gdy
Pole BluetoothSocket
jest pozyskiwane z: BluetoothServerSocket
,
BluetoothServerSocket
można – i powinien – odrzucić, chyba że chcesz
aby mogło akceptować więcej połączeń.
Aby skonfigurować gniazdo serwera i zaakceptować połączenie, wykonaj następujące czynności sekwencja kroków:
Zadzwoń, aby otrzymać
BluetoothServerSocket
listenUsingRfcommWithServiceRecord(String, UUID)
Ciąg znaków to rozpoznawalna nazwa usługi, którą system automatycznie zapisuje w nowym wpisie w bazie danych protokołu Service Discovery Protocol (SDP) na urządzeniu. Nazwa jest dowolna i może być po prostu nazwą Twojej aplikacji. Unikalny identyfikator uniwersalny (UUID) jest także uwzględniony we wpisie SDP. i stanowi podstawę umowy dotyczącej połączenia z urządzeniem klienckim. Ten Przy próbie połączenia się z tym urządzeniem klient będzie miał identyfikator UUID jednoznacznie identyfikują usługę, z którą chce się połączyć. Te Aby połączenie zostało zaakceptowane, identyfikatory UUID muszą być takie same.
Identyfikator UUID to ustandaryzowany 128-bitowy format identyfikatora ciągu znaków używanego do identyfikować informacje. Identyfikator UUID służy do identyfikowania informacji, które należy jest unikalny w systemie lub sieci, ponieważ prawdopodobieństwo, że identyfikator UUID będzie powtarzanie wynosi w rzeczywistości zero. Jest on generowany niezależnie, scentralizowanego organu. W tym przypadku jest on używany do jednoznacznej identyfikacji usługa Bluetooth aplikacji. Aby uzyskać identyfikator UUID do wykorzystania w aplikacji, możesz go użyć spośród wielu przypadkowych do wygenerowania
UUID
w internecie, a następnie zainicjuj Identyfikator UUID zfromString(String)
Zacznij nasłuchiwać próśb o połączenie przez połączenie telefoniczne
accept()
To jest połączenie blokujące. Ten komunikat pojawia się, gdy jedno połączenie zostało lub wystąpił wyjątek. Połączenie jest akceptowane tylko wtedy, gdy urządzenie zdalne wysłało żądanie połączenia zawierające zgodny identyfikator UUID zarejestrowane w tym gniazdie serwera nasłuchu. Jeśli operacja się uda, Funkcja
accept()
zwraca połączone źródłoBluetoothSocket
.Jeśli nie chcesz akceptować dodatkowych połączeń, zadzwoń pod
close()
To wywołanie metody zwalnia gniazdo serwera i wszystkie jego zasoby, ale nie zamyka połączonego elementu
BluetoothSocket
, który został zwrócony przezaccept()
W przeciwieństwie do TCP/IP, RFCOMM zezwala tylko na jednego połączonego klienta na jednocześnie, więc w większości przypadków warto zadzwonić pod numerclose()
.BluetoothServerSocket
natychmiast po zaakceptowaniu podłączonego gniazdka.
Wywołanie accept()
jest wywołaniem blokującym, więc nie wykonuj go w elemencie głównym
wątek UI aktywności. Wykonanie go w innym wątku sprawi, że aplikacja
nadal reagować na inne interakcje użytkowników. Zazwyczaj warto wykonać całą pracę
obejmujący BluetoothServerSocket
lub BluetoothSocket
w nowym wątku
i zarządzane przez Twoją aplikację. Aby przerwać zablokowane połączenie, np. accept()
, zadzwoń pod numer close()
w: BluetoothServerSocket
lub BluetoothSocket
z innego wątku. Notatka
że wszystkie metody w obiekcie BluetoothServerSocket
lub BluetoothSocket
są
i nie tylko w wątkach.
Przykład
Poniżej znajduje się uproszczony wątek komponentu serwera, który akceptuje połączenia przychodzące:
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); } } }
W tym przykładzie wymagane jest tylko jedno połączenie przychodzące, więc gdy tylko
zostanie zaakceptowane połączenie i uzyskano: BluetoothSocket
, aplikacja przekazuje
pozyskał(a) użytkownika BluetoothSocket
w osobnym wątku, zamyka
BluetoothServerSocket
i wyjdzie poza pętlę.
Pamiętaj, że gdy accept()
zwraca BluetoothSocket
, gniazdo jest już
– podłączono Nie należy więc wywoływać
connect()
, tak jak Ty
po stronie klienta.
Metoda manageMyConnectedSocket()
obowiązująca w konkretnej aplikacji służy do inicjowania
wątku dotyczącego przenoszenia danych, który jest omówiony
przenoszenie danych Bluetooth
.
Zazwyczaj należy zamknąć urządzenie BluetoothServerSocket
zaraz po zakończeniu pracy
nasłuchującego połączeń przychodzących. W tym przykładzie funkcja close()
jest wywoływana w najbliższym czasie
wraz z pozyskiwaniem funkcji BluetoothSocket
. Warto też podać publiczny adres URL
w wątku, która może zamknąć prywatny BluetoothSocket
w wydarzeniu
musisz przestać nasłuchiwać w tym gnieździe serwera.
Połącz jako klient
Aby zainicjować połączenie z urządzeniem zdalnym, które akceptuje
połączeń z otwartym gniazdem serwera, musisz najpierw uzyskać BluetoothDevice
który reprezentuje urządzenie zdalne. Aby dowiedzieć się, jak utworzyć
BluetoothDevice
, zobacz Znajdź Bluetooth
urządzeń. Musisz
użyj BluetoothDevice
do uzyskania BluetoothSocket
i zainicjuj
połączenia.
Podstawowa procedura jest następująca:
Użyj karty
BluetoothDevice
, aby otrzymaćBluetoothSocket
, dzwoniąccreateRfcommSocketToServiceRecord(UUID)
.Ta metoda inicjuje obiekt
BluetoothSocket
, który umożliwia klientowi połączyć się z urządzeniemBluetoothDevice
. Przekazany tutaj identyfikator UUID musi być zgodny z używanym identyfikatorem UUID przez urządzenie serwera, gdy została wywołanalistenUsingRfcommWithServiceRecord(String, UUID)
aby ją otworzyć:BluetoothServerSocket
. Aby użyć pasującego identyfikatora UUID, zakoduj na stałe parametr UUID do aplikacji, a potem odwoływanie się do niego zarówno z serwera, i kod klienta.Zainicjuj połączenie, dzwoniąc pod numer
connect()
. Pamiętaj, że ta metoda jest blokując połączenie.Gdy klient wywoła tę metodę, system przeprowadza wyszukiwanie SDP, aby znaleźć urządzenie zdalne z pasującym identyfikatorem UUID. Jeśli wyszukiwanie zakończy się pomyślnie, a urządzenie zdalne zaakceptuje połączenie, współdzieli kanał RFCOMM do używania podczas połączenia, a metoda
connect()
zostanie zwrócona. Jeśli połączenie nie uda się lub po przekroczeniu limitu czasu metodyconnect()
(po około 12 sekundach), metoda zwracaIOException
.
Ponieważ connect()
to wywołanie blokujące, należy je zawsze wykonywać
procedura nawiązywania połączenia w wątku oddzielnym od głównego działania (UI)
w wątku.
Przykład
Oto podstawowy przykład wątku klienta, który inicjuje połączenie Bluetooth połączenie:
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); } } }
Zwróć uwagę, że w tym fragmencie kodu usługa cancelDiscovery()
jest wywoływana przed połączeniem
może być przeprowadzana. Zawsze należy dzwonić pod numer cancelDiscovery()
przed connect()
,
zwłaszcza, że cancelDiscovery()
działa niezależnie od tego, czy urządzenie
trwa odkrywanie. Jeśli aplikacja musi określić,
wykrywanie urządzeń jest w toku, możesz to sprawdzić za pomocą
isDiscovering()
Metoda manageMyConnectedSocket()
obowiązująca w konkretnej aplikacji służy do inicjowania
wątku przenoszenia danych, który jest omówiony w sekcji
przenoszenie danych Bluetooth.
Gdy skończysz korzystać z urządzenia BluetoothSocket
, zawsze dzwoń pod numer close()
. Robię to
powoduje natychmiastowe zamknięcie podłączonego gniazda i zwolnienie wszystkich powiązanych
i zasobami Google Cloud.