3. 1 Aufgabe
1.1 Auftraggeber
Dipl.-Ing. Borchert, im Rahmen der Mobile Computing Vorlesung.
1.2 Auftragnehmer
Andrè Schika, Sven Diesendorf, Martin Reinhardt
2 Zielbestimmungen
2.1 Musskriterien
Das Ziel des Bluetooth Chats muss eine Kommunikation mit mehreren
Clients sein. Außerdem muss die Anwendung sowohl als Server als auch
Client dienen.
2.2 Wunschkriterien
Eine Umsetzung für möglichst viele mobile Endgeräte mit MIDP 2.0
Unterstützung wird angestrebt.
2.3 Abgrenzungskriterien
Was die Anwendung nicht können muss, ist die Unterstützung der Geräte
mit MIDP 1.1.
3 Produkteinsatz
3.1 Anwendungsbereiche
Die Anwendung soll im Bereich Kommunikation mit einer begrenzten
Reichweite liegen. Die Reichweite hängt primär vom Bluetooth
Sender/Empfänger ab.
3.2 Zielgruppen
Die Zielgruppe ist jeder, der ein mobiles Endgerät mit MIDP 2.0 sowie
Bluetooth hat.
-1-
4. 4 Produkt
4.1 Produktübersicht
Unser Produkt ist ein Bluetooth Chat, welcher über die Bluetooth -
Schnittstelle der mobilen Endgeräte kommuniziert.
Unsere Anwendung soll das Chatten mit möglichst vielen Nutzern
realisieren.
Es soll möglich sein Nachrichten mit einer maximalen Länge von 200
Zeichen zu versenden. Die Displayanzeige soll Geräteunabhängig sein.
Die Ansicht des Chatfensters ist unabhängig vom Fenster zum schreiben
von neuen Nachrichten und anderen Informationsfenstern.
4.2 Produktleistungen
Das Programm ist wegen der Übersichtlichkeit und Komplexität in mehrere
Klassen unterteilt.
Um die Kommunikation mit vielen Nutzern zu ermöglichen ist eine Klasse
nötig die alle Informationen der Endgeräte abspeichert.
Weiterhin muss sichergestellt werden, dass alle gespeicherten Daten der
Endgeräte aktuell bleiben. Um dies sicherzustellen wird in kurzen
Zeitintervallen nach den Geräten aus der Liste und neuen gesucht.
Wenn ein Benutzer den Chat verlässt, wird dieser aus der Geräteliste
entfernt und alle aktiven Chatnutzer bekommen dies als Meldung auf den
Display.
5 Technische Vorraussetzungen
Mobiles Endgerät:
- MIDP-2.0
- CLDC-1.1
- Java
- Bluetooth
- Symbian OS
-2-
5. 6 Entwicklungsumgebung
Entwicklungsumgebung:
- J2ME 2.2
- J2SE 1.5.0.06
Entwicklungssoftware:
- Netbeans IDE 4.1 und Mobility Pack
- Eclipse 3.2 und eclipseme-Plugin
7 Benutzeroberfläche
Das Startfenster (Abb. 7.1) und das Fenster (Abb. 7.2) mit der
Eingabeaufforderung des Nicknamens.
Abbildung 7.1 Abbildung 7.2
-3-
6. Einen Chatraum (Abb. 7.3) starten, dabei fungiert das mobile Gerät als
Server, welches den Chat zuerst startet. Auf dem zweiten Screenshot
(Abb.7.4) kann man da Fenster zur Eingabe der zu sendenden Nachricht
sehen.
Abbildung 7.3 Abbildung 7.4
Kommunikation zwischen zwei virtuellen Handys. Hier empfängt Gerät Nr.
1 (Abb. 7.5) die Meldung vom Gerät Nr. 2 (Abb. 7.6).
Abbildung 7.5 Abbildung 7.6
-4-
7. 8 Geschäftsprozessdiagramme
Sich als erster im Chat anmelden
Sich als zweiter im Chat anmelden
Schreiben einer Nachricht
-5-
9. 9 Funktionen
Der Bluetooth-Chat ist in mehrere Klassen und Funktionen aufgeteilt. Die
Funktionen und Klassen erfüllen bestimmte Aufgaben, welche hier
genauer vorgestellt werden.
Hier wird gezeigt wie der Chat funktioniert, es wird der Ablauf dargestellt.
Funktionenübersicht
Die ChatMain-Klasse ist die Hauptklasse der Anwendung. Hier wird die
grafische Oberfläche aufgebaut.
[. . .]
public class ChatMain extends MIDlet implements BTListener, CommandListener
{
[. . .]
public void startApp()
{
// Neue Referenz für das Display
display = Display.getDisplay(this );
// GUI initialisieren
inputui = new InputUI();
messageui = new MessageUI();
nameui = new NameUI();
display.setCurrent( nameui );
}
[. . .]
public void handleAction( String event, Object param1, Object param2 )
{
[. . .]
// Ausgelöste netzwerktechnische Events vom Benutzer abfangen und
// entsprechend reagieren
// JOIN-EVENT – den Chatraum beitretten
// SENT-EVENT – Senden der Nachricht
// RECEIVED-EVENT – Empfangen der Nachricht
// LEAVE-EVENT – Verlassen des Chats
}
public void commandAction(Command c, Displayable d)
-7-
10. {
[. . .]
// Hier werden die Menüpunkte, welche vom Benutzer ausgewählt wurden
// bearbeitet
}
[. . .]
}
Die Klasse NameUI zeichnet das Fenster für die Eingabe des
Nicknamens.
public class NameUI extends Form
{
TextField text;
public NameUI()
{
super("Nicknamen eingeben");
setCommandListener( ChatMain.instance );
addCommand(new Command("Chat", Command.SCREEN, 1));
append( new StringItem( "", "Bitte geben Sie Ihren Nicknamen ein:" ) );
append( text = new TextField( "Dein Nickname", "", 10, TextField.ANY ));
}
}
Die Klasse MessageUI stellt das Chatfenster dar, hier werden die
Nachrichten auf dem Bildschirm dargestellt und man kann hier die
Nachrichten löschen. Alte Nachrichten können nicht angezeigt werden. Da
die Daten in einem Array abgespeichert werden, welcher immer
aktualisiert wird.
public class MessageUI extends Canvas
{
[. . .] //Variablendefinition
public MessageUI()
{
addCommand(new Command("Schreiben", Command.SCREEN, 1));
addCommand(new Command("Loeschen", Command.SCREEN, 2));
addCommand(new Command("Ueber", Command.SCREEN, 3));
addCommand(new Command("Beenden", Command.SCREEN, 4));
setCommandListener( ChatMain.instance );
}
// das Bildschirm nach jeder Nachricht aktualisieren
protected void paint(Graphics g)
{
[. . .]
// Nachricht auf dem Bildschirm darstellen
for ( int i= midx; i< msgs.size(); i++ )
{
ChatPacket p = (ChatPacket)msgs.elementAt(i);
String s = p.sender+": "+p.msg;
g.drawString( s, 0, y, Graphics.BASELINE | Graphics.LEFT );
y += fh;
}
}
-8-
11. //im Chatfenster scrollen
public void keyPressed( int key )
{
[. . .]
}
}
Die Klasse BTListener beinhaltet mögliche Kommandos und reagiert auf
diese.
public interface BTListener
{ public final static String EVENT_JOIN = "join";
public final static String EVENT_LEAVE = "leave";
public final static String EVENT_RECEIVED = "received";
public final static String EVENT_SENT = "sent";
public void handleAction( String action, Object param1, Object param2 );}
Die Chatpacket-Klasse beinhaltet die Nachricht, welche
verpackt über Bluetooth geschickt wird.
public class ChatPacket
{
// ein von NetLayer.SIGNAL_XXX Signalen
public int signal;
// Nickname des Senders
public String sender;
// Inhalt der Nachricht
public String msg;
// Konstruktoren für das Paket
public ChatPacket(int signal, String msg)
{
this.signal = signal; this.msg = msg;
}
public ChatPacket(int signal, String sender, String msg)
{
this.signal = signal; this.sender = sender; this.msg = msg;
}
public ChatPacket() { }
}
Die Klasse EndPoint repräsentiert alle Eigenschaften eines entfernten
mobilen Gerätes und ist für das Senden und Empfangen der Nachrichten
verantwortlich.
public class EndPoint
{
RemoteDevice remoteDev; // Entferntes Gerät
DeviceClass remoteClass; // entferte Klasse des Gerätes
String remoteUrl; // entfernte Service URL
StreamConnection con; // Verbundung zu entfernten Geräten
int transId = -1; // -1 default - Bluetooth DiscoveryID
[. . .]
public EndPoint( NetLayer btnet, RemoteDevice rdev, StreamConnection c )
{
this.btnet = btnet;
remoteDev = rdev;
try
{
-9-
12. remoteName = rdev.getFriendlyName(false); // temp name
}
catch (IOException ex) { remoteName = "Unbekannt"; }
[. . .]
}
// Paket an alle Geräte senden die man gefunden hat
public synchronized void putString( int signal, String s )
{
[. . .]
}
//Nachricht von der Gegenseite empfangen (alle Geräte, die was schicken)
public synchronized ChatPacket getString()
{
[. . .]
}
[. . .]
}
Die InputUI stellt das Eingabefenster für die Nachricht dar.
Die Nachricht hat eine maximale Länge von 200 Zeichen.
public class InputUI extends TextBox
{
public InputUI()
{
super("Nachricht eingeben", "", 200, TextField.ANY);
addCommand(new Command("Senden", Command.SCREEN, 1));
addCommand(new Command("Zurueck", Command.SCREEN, 1));
setCommandListener( ChatMain.instance );
}
public void showUI()
{
this.setString("");
}
}
Die Klasse NetLayer behandelt die Bluetooth Konvektivitäten und den
Geräte/Service Such-Prozess. In dieser Klasse wird nach Geräten gesucht,
lokaler Server erstellt, ankommende Verbindungen werden entsprechend
behandelt und Verbindungen zu entfernten Geräten hergestellt.
[. . .]
public class NetLayer implements Runnable
{
public final static int SIGNAL_HANDSHAKE = 0;
public final static int SIGNAL_MESSAGE = 1;
public final static int SIGNAL_TERMINATE = 3;
public final static int SIGNAL_HANDSHAKE_ACK = 4;
public final static int SIGNAL_TERMINATE_ACK = 5;
// spezielle UUID
private final static UUID uuid = new
UUID("102030405060708090A0B0C0D0E0F010", false);
// Hauptklasse als SERVICE_TELEPHONY
private final static int SERVICE_TELEPHONY = 0x400000;
LocalDevice localDevice = null;
DiscoveryAgent agent = null;
StreamConnectionNotifier server;
BTListener callback = null;
boolean done = false;
- 10 -
13. String localName = "";
Vector endPoints = new Vector();
Vector pendingEndPoints = new Vector();
Hashtable serviceRecordToEndPoint = new Hashtable();
Object lock = new Object();
Timer timer = new Timer();
public NetLayer()
{
}
public void init(String name, BTListener callback)
{
try
{
[. . .]
localDevice = LocalDevice.getLocalDevice();
localDevice.setDiscoverable(DiscoveryAgent.GIAC);
agent = localDevice.getDiscoveryAgent();
Thread thread = new Thread( this );
thread.start();
[. . .]
}
public void disconnect()
{
// Server stoppen, keine Clients akzeptieren
[. . .]
}
}
[. . .]
public EndPoint findEndPointByRemoteDevice( RemoteDevice rdev )
{
for ( int i=0; i < endPoints.size(); i++ )
{
EndPoint endpt = (EndPoint) endPoints.elementAt( i );
if ( endpt.remoteDev.equals( rdev ) )
{
return endpt;
}
}
return null;
}
public EndPoint findEndPointByTransId( int id )
{
for ( int i=0; i < pendingEndPoints.size(); i++ )
{
EndPoint endpt = (EndPoint) pendingEndPoints.elementAt( i );
if ( endpt.transId == id )
{
return endpt;
}
}
return null;
}
/**
* Nachricht an alle bekannten geräte abschicken
*/
- 11 -
14. public void sendString( String s )
{
for ( int i=0; i < endPoints.size(); i++ )
{
EndPoint endpt = (EndPoint) endPoints.elementAt( i );
endpt.putString( NetLayer.SIGNAL_MESSAGE, s );
}
}
/**
* Ressourcen freigeben, wenn Gerät "verloren" ist
*/
public void cleanupRemoteEndPoint( EndPoint endpt )
{
endpt.reader.stop();
endpt.sender.stop();
endPoints.removeElement( endpt );
}
/**
* Implementiert den lokalen BTC
*/
public void run()
{
// Verbindung herstellen
[. . .]
while( !done)
{
try
{
ChatMain.instance.gui_log( "", "Warte auf Verbindungen ..." );
c = server.acceptAndOpen();
RemoteDevice rdev = RemoteDevice.getRemoteDevice( c );
EndPoint endpt = findEndPointByRemoteDevice( rdev );
if ( endpt != null )
{
//TODO
}
else
{
endpt = new EndPoint( this, rdev, c);
Thread t1 = new Thread( endpt.sender );
t1.start();
Thread t2 = new Thread( endpt.reader );
t2.start();
endPoints.addElement( endpt );
}
}
catch (IOException e)
{
[. . .]
}
finally
{
// TODO
}
}
}
/**
* Eigener Discoverly Listener um Geräte und Services abzufangen.
*/
- 12 -
15. [. . .]
/**
* Nach neuen Geräten 100 ms lang suchen.
*/
public void inquiryCompleted(int transId)
{
timer.schedule( new DoServiceDiscovery(), 100 );
}
/**
* Ein Service ist vom entfernten Gerät entdeckt. Verbinde dich mit dem
Gerät.
*/
public void servicesDiscovered(int transId, ServiceRecord[] svcRec)
{
[. . .]
}
/**
* Service Discovery ist abgeschlossen.
*/
public void serviceSearchCompleted(int transID, int respCode)
{
[. . .]
}
}
class DoServiceDiscovery extends TimerTask
{
public void run()
{
[. . .]
}
}
}
Die Klasse Reader ist für das Lesen der Daten und ihre Verarbeitung
verantwortlich.
[. . .]
public class Reader implements Runnable
{
[. . .]
public void run()
{
try
{
DataInputStream datain = endpt.con.openDataInputStream();
while ( !done )
{
int signal = datain.readInt();
if ( signal == NetLayer.SIGNAL_MESSAGE )
{
String s = datain.readUTF();
ChatPacket packet = new ChatPacket( NetLayer.SIGNAL_MESSAGE,
endpt.remoteName, s );
endpt.callback.handleAction( BTListener.EVENT_RECEIVED, endpt,
packet );
- 13 -
16. }
else if ( signal == NetLayer.SIGNAL_HANDSHAKE )
{
String s = datain.readUTF();
endpt.remoteName = s;
endpt.putString( NetLayer.SIGNAL_HANDSHAKE_ACK, endpt.localName );
endpt.callback.handleAction( BTListener.EVENT_JOIN, endpt, null );
}
[. . .]
}
}
Die Klasse Sender sendet die Daten über die hergestellte Verbindung an
alle Geräte.
[. . .]
public class Sender implements Runnable
{
public EndPoint endpt;
private boolean done = false;
public Sender()
{
}
public void stop()
{
done = true;
}
public void run()
{
try
{
DataOutputStream dataout = endpt.con.openDataOutputStream();
while( !done )
{
if ( ! endpt.peekString() )
{
synchronized (this)
{ this.wait(5000); }
}
ChatPacket s = endpt.getString();
if ( s != null )
{
dataout.writeInt(s.signal);
dataout.writeUTF(s.msg );
dataout.flush();
}
if ( s != null && s.signal == NetLayer.SIGNAL_TERMINATE )
{ stop(); }
}
dataout.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}}
- 14 -
17. 10 Problematik
Folgende Probleme sind während der Entwicklung aufgetreten:
1.
T630 war nicht mit MIDP 2.0 Kompatibel, wodurch die Entwicklung des
Programms nur auf Nokia fortgesetzt wurde
2.
Verbindungsprobleme mit Bluetooth, hier war das finden der Geräte am
Anfang ein Problem.
3.
Übertragungsprobleme mit Bluetooth, hier hatten wir das Problem mit der
Übertragung von Zeichen von einem Gerät zu dem anderem.
4.
Zusammenbau der Oberfläche und der Funktionen, hier musste die
Oberfläche etwas vereinfacht werden.
5.
Speicherung von gefunden Geräten in Vektoren und deren Abruf aus
diesen Vektoren.
- 15 -