Linguaggio
Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo argomento fornisce una panoramica di RFCOMM Bluetooth nelle app Windows, insieme al codice di esempio su come inviare o ricevere un file.
- APIimportanti:Windows. Devices.Bluetooth, Windows. Devices.Bluetooth.Rfcomm
Important
È necessario dichiarare la funzionalità "bluetooth" in Package.appxmanifest.
<Capabilities> <DeviceCapability Name="bluetooth" /> </Capabilities>
Overview
Le API nello spazio dei nomi Windows.Devices.Bluetooth.Rfcomm si basano sui modelli esistenti per Windows.Devices, tra cui enumerazione e istanziazione. La lettura e la scrittura dei dati sono progettate per sfruttare established data stream patterns e oggetti in Windows. Storage.Streams. Gli attributi SDP (Service Discovery Protocol) hanno un valore e un tipo previsto. Tuttavia, alcuni dispositivi comuni hanno implementazioni difettose degli attributi SDP in cui il valore non è del tipo previsto. Inoltre, molti utilizzi di RFCOMM non richiedono attributi SDP aggiuntivi. Per questi motivi, questa API offre l'accesso ai dati SDP non verificati, da cui gli sviluppatori possono ottenere le informazioni necessarie.
Le API RFCOMM usano il concetto di identificatori di servizio. Anche se un identificatore del servizio è semplicemente un GUID a 128 bit, viene anche specificato comunemente come intero a 16 o a 32 bit. L'API RFCOMM offre un wrapper per gli identificatori di servizio che consente di specificarli e utilizzarli come GUID a 128 bit, nonché interi a 32 bit, ma non offre numeri interi a 16 bit. Questo non è un problema per l'API perché le lingue eseguiranno automaticamente l'upsize a un intero a 32 bit e l'identificatore può comunque essere generato correttamente.
Le app possono eseguire operazioni sul dispositivo in più passaggi in un'attività in background in modo che possano essere eseguite fino al completamento anche se l'app viene spostata in background e sospesa. Ciò consente la manutenzione affidabile dei dispositivi, ad esempio le modifiche alle impostazioni permanenti o al firmware, e la sincronizzazione del contenuto, senza che l'utente sieda e osservi un indicatore di stato. Usare DeviceServicingTrigger per la manutenzione dei dispositivi e DeviceUseTrigger per la sincronizzazione del contenuto. Si noti che queste attività in background vincolano la quantità di tempo in cui l'app può essere eseguita in background e non sono destinate a consentire l'operazione illimitata o la sincronizzazione infinita.
Per un esempio di codice completo che descrive in dettaglio l'operazione RFCOMM, vedere Bluetooth Rfcomm Chat Sample su Github.
Inviare un file come client
Quando si invia un file, lo scenario più semplice consiste nel connettersi a un dispositivo associato in base a un servizio desiderato. Questa operazione prevede i passaggi seguenti:
- Usare le funzioni RfcommDeviceService.GetDeviceSelector* per facilitare la generazione di una query AQS (Advanced Query Syntax) che può essere usata per enumerare le istanze dei dispositivi associati per il servizio desiderato.
- Selezionare un dispositivo enumerato, creare un RfcommDeviceService e leggere gli attributi SDP secondo necessità (usando gli helper di dati consolidati per analizzare i dati dell'attributo).
- Creare un socket e usare le proprietà RfcommDeviceService.ConnectionHostName e RfcommDeviceService.ConnectionServiceName in StreamSocket.ConnectAsync al servizio dispositivo remoto con i parametri appropriati.
- Seguire i modelli consolidati per i flussi di dati per leggere blocchi di dati dal file e inviarli al dispositivo tramite lo StreamSocket.OutputStream del socket.
using System;
using System.Threading.Tasks;
using Windows.Devices.Bluetooth.Rfcomm;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using Windows.Devices.Bluetooth;
Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService _service;
Windows.Networking.Sockets.StreamSocket _socket;
async void Initialize()
{
// Enumerate devices with the object push service
var services =
await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(
RfcommDeviceService.GetDeviceSelector(
RfcommServiceId.ObexObjectPush));
if (services.Count > 0)
{
// Initialize the target Bluetooth BR device
var service = await RfcommDeviceService.FromIdAsync(services[0].Id);
bool isCompatibleVersion = await IsCompatibleVersionAsync(service);
// Check that the service meets this App's minimum requirement
if (SupportsProtection(service) && isCompatibleVersion)
{
_service = service;
// Create a socket and connect to the target
_socket = new StreamSocket();
await _socket.ConnectAsync(
_service.ConnectionHostName,
_service.ConnectionServiceName,
SocketProtectionLevel
.BluetoothEncryptionAllowNullAuthentication);
// The socket is connected. At this point the App can wait for
// the user to take some action, for example, click a button to send a
// file to the device, which could invoke the Picker and then
// send the picked file. The transfer itself would use the
// Sockets API and not the Rfcomm API, and so is omitted here for
// brevity.
}
}
}
// This App requires a connection that is encrypted but does not care about
// whether it's authenticated.
bool SupportsProtection(RfcommDeviceService service)
{
switch (service.ProtectionLevel)
{
case SocketProtectionLevel.PlainSocket:
if ((service.MaxProtectionLevel == SocketProtectionLevel
.BluetoothEncryptionWithAuthentication)
|| (service.MaxProtectionLevel == SocketProtectionLevel
.BluetoothEncryptionAllowNullAuthentication))
{
// The connection can be upgraded when opening the socket so the
// App may offer UI here to notify the user that Windows may
// prompt for a PIN exchange.
return true;
}
else
{
// The connection cannot be upgraded so an App may offer UI here
// to explain why a connection won't be made.
return false;
}
case SocketProtectionLevel.BluetoothEncryptionWithAuthentication:
return true;
case SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication:
return true;
}
return false;
}
// This App relies on CRC32 checking available in version 2.0 of the service.
const uint SERVICE_VERSION_ATTRIBUTE_ID = 0x0300;
const byte SERVICE_VERSION_ATTRIBUTE_TYPE = 0x0A; // UINT32
const uint MINIMUM_SERVICE_VERSION = 200;
async Task<bool> IsCompatibleVersionAsync(RfcommDeviceService service)
{
var attributes = await service.GetSdpRawAttributesAsync(
BluetoothCacheMode.Uncached);
var attribute = attributes[SERVICE_VERSION_ATTRIBUTE_ID];
var reader = DataReader.FromBuffer(attribute);
// The first byte contains the attribute's type
byte attributeType = reader.ReadByte();
if (attributeType == SERVICE_VERSION_ATTRIBUTE_TYPE)
{
// The remainder is the data
uint version = reader.ReadUInt32();
return version >= MINIMUM_SERVICE_VERSION;
}
else return false;
}
...
#include <winrt/Windows.Devices.Bluetooth.Rfcomm.h>
#include <winrt/Windows.Devices.Enumeration.h>
#include <winrt/Windows.Networking.Sockets.h>
#include <winrt/Windows.Storage.Streams.h>
...
Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService m_service{ nullptr };
Windows::Networking::Sockets::StreamSocket m_socket;
Windows::Foundation::IAsyncAction Initialize()
{
// Enumerate devices with the object push service.
Windows::Devices::Enumeration::DeviceInformationCollection services{
co_await Windows::Devices::Enumeration::DeviceInformation::FindAllAsync(
Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService::GetDeviceSelector(
Windows::Devices::Bluetooth::Rfcomm::RfcommServiceId::ObexObjectPush())) };
if (services.Size() > 0)
{
// Initialize the target Bluetooth BR device.
Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService service{
co_await Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService::FromIdAsync(services.GetAt(0).Id()) };
// Check that the service meets this App's minimum
// requirement
if (SupportsProtection(service)
&& co_await IsCompatibleVersion(service))
{
m_service = service;
// Create a socket and connect to the target
co_await m_socket.ConnectAsync(
m_service.ConnectionHostName(),
m_service.ConnectionServiceName(),
Windows::Networking::Sockets::SocketProtectionLevel::BluetoothEncryptionAllowNullAuthentication);
// The socket is connected. At this point the App can
// wait for the user to take some action, for example, click
// a button to send a file to the device, which could
// invoke the Picker and then send the picked file.
// The transfer itself would use the Sockets API and
// not the Rfcomm API, and so is omitted here for
//brevity.
}
}
}
// This App requires a connection that is encrypted but does not care about
// whether it's authenticated.
bool SupportsProtection(Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService const& service)
{
switch (service.ProtectionLevel())
{
case Windows::Networking::Sockets::SocketProtectionLevel::PlainSocket:
if ((service.MaxProtectionLevel() == Windows::Networking::Sockets::SocketProtectionLevel::BluetoothEncryptionWithAuthentication)
|| (service.MaxProtectionLevel() == Windows::Networking::Sockets::SocketProtectionLevel::BluetoothEncryptionAllowNullAuthentication))
{
// The connection can be upgraded when opening the socket so the
// App may offer UI here to notify the user that Windows may
// prompt for a PIN exchange.
return true;
}
else
{
// The connection cannot be upgraded so an App may offer UI here
// to explain why a connection won't be made.
return false;
}
case Windows::Networking::Sockets::SocketProtectionLevel::BluetoothEncryptionWithAuthentication:
return true;
case Windows::Networking::Sockets::SocketProtectionLevel::BluetoothEncryptionAllowNullAuthentication:
return true;
}
return false;
}
// This application relies on CRC32 checking available in version 2.0 of the service.
const uint32_t SERVICE_VERSION_ATTRIBUTE_ID{ 0x0300 };
const byte SERVICE_VERSION_ATTRIBUTE_TYPE{ 0x0A }; // UINT32.
const uint32_t MINIMUM_SERVICE_VERSION{ 200 };
Windows::Foundation::IAsyncOperation<bool> IsCompatibleVersion(Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService const& service)
{
auto attributes{
co_await service.GetSdpRawAttributesAsync(Windows::Devices::Bluetooth::BluetoothCacheMode::Uncached) };
auto attribute{ attributes.Lookup(SERVICE_VERSION_ATTRIBUTE_ID) };
auto reader{ Windows::Storage::Streams::DataReader::FromBuffer(attribute) };
// The first byte contains the attribute's type.
byte attributeType{ reader.ReadByte() };
if (attributeType == SERVICE_VERSION_ATTRIBUTE_TYPE)
{
// The remainder is the data
uint32_t version{ reader.ReadUInt32() };
co_return (version >= MINIMUM_SERVICE_VERSION);
}
}
...
Ricezione file in qualità di server
Un altro scenario comune di app RFCOMM consiste nell'ospitare un servizio nel PC ed esporlo per altri dispositivi.
- Creare un RfcommServiceProvider per annunciare il servizio desiderato.
- Impostare gli attributi SDP secondo necessità (usando helper di dati predefiniti per generare i dati dell'attributo) e avviare la pubblicazione dei record SDP affinché altri dispositivi possano recuperarli.
- Per connettersi a un dispositivo client, creare un listener socket per avviare l'ascolto delle richieste di connessione in ingresso.
- Quando viene ricevuta una connessione, archiviare il socket connesso per un'elaborazione successiva.
- Seguire i modelli del flusso di dati stabiliti per leggere blocchi di dati da InputStream del socket e salvarli in un file.
Per rendere persistente un servizio RFCOMM in background, usare RfcommConnectionTrigger. L'attività in background viene attivata alla connessione al servizio. Lo sviluppatore riceve un riferimento al socket nell'attività in background. L'attività in background è di lunga durata e rimane attiva finché il socket è in uso.
Windows.Devices.Bluetooth.Rfcomm.RfcommServiceProvider _provider;
async void Initialize()
{
// Initialize the provider for the hosted RFCOMM service
_provider =
await Windows.Devices.Bluetooth.Rfcomm.RfcommServiceProvider.CreateAsync(
RfcommServiceId.ObexObjectPush);
// Create a listener for this service and start listening
StreamSocketListener listener = new StreamSocketListener();
listener.ConnectionReceived += OnConnectionReceivedAsync;
await listener.BindServiceNameAsync(
_provider.ServiceId.AsString(),
SocketProtectionLevel
.BluetoothEncryptionAllowNullAuthentication);
// Set the SDP attributes and start advertising
InitializeServiceSdpAttributes(_provider);
_provider.StartAdvertising(listener);
}
const uint SERVICE_VERSION_ATTRIBUTE_ID = 0x0300;
const byte SERVICE_VERSION_ATTRIBUTE_TYPE = 0x0A; // UINT32
const uint SERVICE_VERSION = 200;
void InitializeServiceSdpAttributes(RfcommServiceProvider provider)
{
Windows.Storage.Streams.DataWriter writer = new Windows.Storage.Streams.DataWriter();
// First write the attribute type
writer.WriteByte(SERVICE_VERSION_ATTRIBUTE_TYPE);
// Then write the data
writer.WriteUInt32(MINIMUM_SERVICE_VERSION);
IBuffer data = writer.DetachBuffer();
provider.SdpRawAttributes.Add(SERVICE_VERSION_ATTRIBUTE_ID, data);
}
void OnConnectionReceivedAsync(
StreamSocketListener listener,
StreamSocketListenerConnectionReceivedEventArgs args)
{
// Stop advertising/listening so that we're only serving one client
_provider.StopAdvertising();
listener.Dispose();
_socket = args.Socket;
// The client socket is connected. At this point the App can wait for
// the user to take some action, for example, click a button to receive a file
// from the device, which could invoke the Picker and then save the
// received file to the picked location. The transfer itself would use
// the Sockets API and not the Rfcomm API, and so is omitted here for
// brevity.
}
...
#include <winrt/Windows.Devices.Bluetooth.Rfcomm.h>
#include <winrt/Windows.Networking.Sockets.h>
#include <winrt/Windows.Storage.Streams.h>
...
Windows::Devices::Bluetooth::Rfcomm::RfcommServiceProvider m_provider{ nullptr };
Windows::Networking::Sockets::StreamSocket m_socket;
Windows::Foundation::IAsyncAction Initialize()
{
// Initialize the provider for the hosted RFCOMM service.
auto provider{ co_await Windows::Devices::Bluetooth::Rfcomm::RfcommServiceProvider::CreateAsync(
Windows::Devices::Bluetooth::Rfcomm::RfcommServiceId::ObexObjectPush()) };
m_provider = provider;
// Create a listener for this service and start listening.
Windows::Networking::Sockets::StreamSocketListener listener;
listener.ConnectionReceived({ this, &MainPage::OnConnectionReceived });
co_await listener.BindServiceNameAsync(m_provider.ServiceId().AsString(),
Windows::Networking::Sockets::SocketProtectionLevel::BluetoothEncryptionAllowNullAuthentication);
// Set the SDP attributes and start advertising
InitializeServiceSdpAttributes();
m_provider.StartAdvertising(listener);
}
const uint32_t SERVICE_VERSION_ATTRIBUTE_ID{ 0x0300 };
const byte SERVICE_VERSION_ATTRIBUTE_TYPE{ 0x0A }; // UINT32.
const uint32_t SERVICE_VERSION{ 200 };
void InitializeServiceSdpAttributes()
{
Windows::Storage::Streams::DataWriter writer;
// First write the attribute type
writer.WriteByte(SERVICE_VERSION_ATTRIBUTE_TYPE);
// Then write the data
writer.WriteUInt32(SERVICE_VERSION);
auto data{ writer.DetachBuffer() };
m_provider.SdpRawAttributes().Insert(SERVICE_VERSION_ATTRIBUTE_ID, data);
}
void OnConnectionReceived(
Windows::Networking::Sockets::StreamSocketListener const& listener,
Windows::Networking::Sockets::StreamSocketListenerConnectionReceivedEventArgs const& args)
{
// Stop advertising/listening so that we're only serving one client
m_provider.StopAdvertising();
listener.Close();
m_socket = args.Socket();
// The client socket is connected. At this point the application can wait for
// the user to take some action, for example, click a button to receive a
// file from the device, which could invoke the Picker and then save
// the received file to the picked location. The transfer itself
// would use the Sockets API and not the Rfcomm API, and so is
// omitted here for brevity.
}
...