We can transfer data to another device directly without having to use the Internet or any network infrastructure outside another device. Bluetooth allows one device to directly communicate with another device.
We can set up our app to listen for Bluetooth devices that we can communicate with:
Bluetooth
and BluetoothAdmin
, before we can transmit data via Bluetooth:[assembly: UsesPermission(Manifest.Permission.Bluetooth)] [assembly: UsesPermission(Manifest.Permission.BluetoothAdmin)]
const string ServiceName = "XamarinCookbookBluetooth";
const string Uuid = "25c0d296-0e78-4849-b70b-86f01f415add";
ListenUsingRfcommWithServiceRecord()
method on the Bluetooth adapter, passing the service registration name and the UUID for this app:BluetoothServerSocket serverSocket = null; BluetoothSocket serverClientSocket = null; try { serverSocket = bluetooth.ListenUsingRfcommWithServiceRecord( ServiceName, UUID.FromString(Uuid)); while (serverClientSocket == null) { serverClientSocket = await serverSocket.AcceptAsync(); } } catch (IOException ex) { // there was an error, such as no devices found }
if (serverClientSocket != null) { serverSocket.Close(); var device = serverClientSocket.RemoteDevice; try { inputStream = socket.InputStream; outputStream = socket.OutputStream; } catch (IOException ex) { // handle errors } }
Setting up the app to connect to another device that is listening is much the same, except we request to connect to a specific device:
BluetoothDevice paired = bluetooth.BondedDevices.First();
bluetooth.CancelDiscovery();
BluetoothSocket clientSocket = null; clientSocket = paired.CreateRfcommSocketToServiceRecord( UUID.FromString(Uuid)); await clientSocket.ConnectAsync();
try { inputStream = clientSocket.InputStream; outputStream = clientSocket.OutputStream; } catch (IOException ex) { // handle errors }
On both the server listener and the client, we can transfer data back and forth using the two streams:
byte[] bytes = new byte[1024]; while (true) { try { var size = await inputStream.ReadAsync( bytes, 0, bytes.Length); var stringRead = Encoding.UTF8.GetString( bytes, 0, size); } catch (IOException ex) { // handle errors } }
var stringToSend = "Hello World!"; var bytes = Encoding.UTF8.GetBytes(stringToSend); try { await outputStream.WriteAsync(bytes, 0, bytes.Length); } catch (IOException ex) { // handle errors }
In order to send data from one device to another, both devices need to have an open stream. One device will send data, and the other will listen for incoming data. Also, both devices need to use an UUID that represents the connection from the client. The server listens for an incoming connection with a particular UUID and then accepts it, if it is the expected one.
The UUID is app-specific in that it is controlled by the app. Some apps may randomly generate UUIDs each time, and others may have a hardcoded value. This UUID can be randomly generated by the app, or specified by some other source, such as a new UUID for each major app version.
More information on how Bluetooth actually transfers data can be found on the official Bluetooth website: http://www.bluetooth.com/Pages/How-It-Works.aspx.
The server, or the listener, starts listening for incoming connections via the ListenUsingRfcommWithServiceRecord()
method on the Bluetooth adapter. This method returns a BluetoothServerSocket
with which we try and accept an incoming connection using the AcceptAsync()
method. As soon as an incoming client connection is accepted, the method returns a BluetoothSocket
instance, which represents an open connection.
Once we have an open connection to the client, we can stop listening for more connections by invoking the Close()
method on the BluetoothServerSocket
instance. We can then query the open client connection for the device by using the RemoteDevice
property as well as get the communication streams.
The connection has two streams, one for incoming data, from the InputStream
property, and one for outgoing data, from the OutputStream
property. Both are basic Stream
types that support byte-based communication.
If we want to connect to a listening server, we first need a device to which we want to connect. Once we have the device, either after a discovery or from an existing pair, we invoke the CreateRfcommSocketToServiceRecord()
method on the BluetoothDevice
instance. This method returns a BluetoothSocket
instance, which represents a connection to the server. In order to start communicating, we have to open the connection using the ConnectAsync()
method.
Once the connection to the server is opened, we can obtain the two streams just as we did with the server. Again, the InputStream
instance represents the incoming data channel and the OutputStream
represents the outgoing data channel.
Reading data from the input stream is simply a matter of continuously reading the data out of the InputStream
instance. To do this, we can invoke the ReadAsync()
method, with a buffer, and wait for a response. As soon as enough data is read, either when the buffer is full or when there is no more to read, the method will return with the number of bytes read. We can then convert those bytes into a stream. We could also read the data using a Stream
instance by passing the actual stream to a deserializer or any reader that accepts a Stream
instance such as XDocument.Load()
.
Sending data to the other device is simply a matter of writing bytes to the OutputStream
instance. We could write directly using the WriteAsync()
method or we could use a serializer, such as XDocument.Save()
, which accepts a Stream
instance.