Bluetooth Example 3
 
 

 

Forth example: Peer-to-Peer Communication

Purpose: In many situations two nodes in a network environment want to establish a mutual connection to exchange data without any rules which one of the nodes initiates the communication link. In this case it is not clear which one will play the role of the server or the client. This kind of networking model is often called peer-to-peer communication. Behind the scene the client-server model still applies, but each node may play the role of a server or a client. (The third example that implements a chat system is a typical case where peer-to-peer communication is used.)

To simplify a peer-to-peer implementation the ch.aplu.bluetooth package contains the class BluetoothPeer. The idea is straightforward: The constructor takes the partner's Bluetooth name and tries to connect (as client) to the given node. When the constructor returns, either the connection is established or the node waits for incoming calls (as server). Independent of who is client or server, each node sends a block of data using sendDataBlock(). When the data block arrives, the callback method receiveDataBlock() declared in the BtPeerListener interface is triggered.

In our example a mobile phone (running J2ME) connects to a desktop PC (running J2SE). The phone reclaims some service from the remote PC, in our demonstration just to reverse a word. In a more practical situation the PC could connect to the Internet or a database to send back some more useful information.

Due to the Gidlet framework the code for the MIDlet is pretty short and well arraged.

// BtPeerInqGidlet.java
// Inquiry Machine, use BtPeerAns as 'Answer Machine' on a PC

import
 ch.aplu.bluetooth.*;
import
 ch.aplu.gidlet.*;
import
 javax.microedition.lcdui.*;

public
 class BtPeerInqGidlet extends Gidlet implements BtPeerListener
{
  
private BluetoothPeer bp = null;
  
private String btName = "";
  
private final String serviceName = "WordInvert";
  
private TextField tf;
  
private StringItem it;
  
private MForm form;
  
private volatile boolean isConnected = false;
  
private volatile boolean ok = false;

  
public void main()
  
{
    
// Read from StringStore to avoid fingertips
    StringStore store 
= new StringStore("InqStore");
    
String content = store.read();
    
if (content != null)
      btName 
= content;
    form 
= new MForm("Inquiry machine");
    tf 
= form.addInputField("Enter Bluetooth name:", btName);
    it 
= form.addOutputField();
    form.
show();
    
while (!ok)
      
waitOk();  // Wait for Bluetooth name entry

    store.
write(btName);
    it.
setText("Trying to connect to\n" + btName +
                
"\nwith service\n" + serviceName);

    bp 
= new BluetoothPeer(btName, serviceName, thisfalse);
    
if (!bp.isConnected())  // We are a server, wait for a connection
      
putSleep();
    form 
= new MForm("String Inverter");
    tf 
= form.addInputField("Enter a string:""");
    it 
= form.addOutputField();
    form.
addOkButton();
    form.
show();
  
}

  
// Callback when data are received
  
public void receiveDataBlock(int[] data)
  
{
    
String s = "";
    
int size = data.length;
    
for (int i = 0; i < size; i++)
      s 
= s + (char)data[i];
    it.
setText("Reply: " + s);
    
wakeUp();
  
}

  
// Callback when connection is established or lost
  
public void notifyConnection(boolean connected)
  
{
    
if (connected)
    
{
      isConnected 
= true;
      it.
setText("Connection established");
      
delay(2000);
      
wakeUp();
    
}
    
else
    
{
      isConnected 
= false;
      it.
setText("Connection lost");
      form.
removeOkButton();
      bp.
releaseConnection();
    
}
  
}

  
// Callback when OK soft button is hit
  
public void doOk()
  
{
    
if (!isConnected)  // First form
    
{
      btName 
= form.getText(tf);
      
if (btName.equals(""))
        it.
setText("Illegal entry");
      
else
        ok 
= true;
    
}
    
else // Second form
    
{
      
String value = form.getText(tf);
      
int size = value.length();
      
int[] data = new int[size];
      
for (int i = 0; i < size; i++)
        data
[i] = value.charAt(i);
      bp.
sendDataBlock(data);
      
putSleep(); // Wait for reply
    
}
  
}

  
// Callback when the Exit soft button is hit
  
public void doExit()
  
{
    
if (bp != null)
      bp.
releaseConnection();
    
super.doExit();
  
}
}


Download JAR/JAD files for installation on mobile phone. (You can install it directly on your mobile phone from http://www.aplu.ch/g.)

Discussion: Again we use the RMS to store the Bluetooth name from one invocation to the other. After running the constructor of BluetoothPeer we check if the connection is already established or if we have to wait for the partner. In the latter case the main-thread is put to sleep until the partner connects and notifyConnection(true) is triggered. Then we wake up the main-thread and display the form with a text field where the user can enter the word to invert. The OK button is used for two different purposes: First to confirm the entry of the Bluetooth name and then to engage the data transfer. After the word is sent, we go to sleep again until the reply from the remote node arrives.

It would be more elegant to use generics for the data transfer method receiveDataBlock(), so that an array of any of the primitive data types could be used, but generics are not supported in J2ME.

The partner program runs under J2SE and uses for convenience a Console from the package ch.aplu.util to display what is going on. You may use another GUI of your choice. When you set isVerbose to true, interesting debugging information of the underlaying library calls is displayed that also includes information about the Bluetooth device and service search.

// BtPeerAns.java
// Answer Machine, use BtPeerInq or BtPeerInqGidlet as 'Inquiry Machine'

import
 javax.swing.JOptionPane;
import
 ch.aplu.bluetooth.*;
import
 ch.aplu.util.*;

public
 class BtPeerAns implements BtPeerListener, ExitListener
{
  
private final boolean isVerbose = true;
  
private BluetoothPeer bp;
  
private final String serviceName = "WordInvert";
 
  
public BtPeerAns()
  
{
    
String prompt = "Bluetooth Name?";
    
String partnerName;
    
do
    
{
      partnerName 
= JOptionPane.showInputDialog(null, prompt);
      
if (partnerName == null)
        
System.exit(0);
    
}
    
while (partnerName.trim().length() == 0);
    
    Console c 
= new Console();
    c.
addExitListener(this);

    
System.out.println("Trying to connect to '" + partnerName + 
                       
"' with service '" + serviceName + "'");
    bp 
= new BluetoothPeer(partnerName, serviceName, this, isVerbose);
  
}

  
public void receiveDataBlock(int[] data)
  
{
    
int size = data.length;
    
int[] reply = new int[size];
    
for (int i = 0; i < size; i++)
      reply
[size - 1 - i] = data[i]; // Reverse word
    bp.
sendDataBlock(reply);  // and send it back

    
// Show what we did
    
String in = "";
    
String out = "";
    
for (int i = 0; i < size; i++)
      in 
= in + (char)data[i];
    
for (int i = 0; i < size; i++)
      out 
= out + (char)reply[i];
    
System.out.println("Received: " + in + " - Replied: " + out);
  
}

  
public void notifyConnection(boolean connected)
  
{
    
if (connected)
      
System.out.println("Connection established.");
    
else
      
System.out.println("Connection lost.");
  
}

  
// Called when the close button is hit
  
public void notifyExit()
  
{
    bp.
releaseConnection();
    
System.exit(0);
  
}

  
public static void main(String[] args)
  
{
    
new BtPeerAns();
  
}
}


Execute BtPeerAns using WebStart (Widcomm compatible Bluetooth device needed)
Download BtPeerAns standalone version

Discussion: Check to see that BtPeerInq and BtPeerAns can be started in any order. Check also that the user is politely informed when his partner quits.