Praktikum: Spezifikation - Technische Informatik / Eingebettete

Werbung
1
Seminar zum Programmierprojekt SS 2008
Uni Tübingen, Arbeitsbereich Technische Informatik
Ausgabe: 20. Mai 2008
Anleitung C5
Einführung in die BT-Server-Programmierung mit JavaSE
Allgemeines
Mit Aufgabe 5 beginnen Sie die Implementierungsphase des Praktikums. Auf den Praktikumsrechnern 2 (3, 4) ist Java2SE und Eclipse installiert. Sie müssen für diese Aufgabe den
Bluetooth-Dongle in einen USB-Port einstecken. Sie lernen in Aufgabe 5 die Grundstruktur
eines BT-Servers kennen.
JAVA Bluetooth-Programmierung im HOST
In dieser Programmieraufgabe sollen die Grundlagen der Bluetooth-Programmierung erlernt
werden. Als Ziel für den gesamten Aufgabenblock C5 bis C7 sei die Programmierung eines
Servers im Host gesetzt, der selbst drei Dienste anbietet, nämlich den Dienst "TelefonDienst", der auf Anfrage eine Telefonnummer (z.B. des Sekretariats der TI), den "Gebäudeplan-Dienst", der einen Gebäudeplan an das anfragende Gerät schickt und den „Quiz-Dienst“:
ein kleines Frage- und Antwort-Spiel. Zusätzlich soll ein Dienst "Send Services" als Antwort
auf eine Dienstanfrage eines entfernten Gerätes die im Host registrierten Dienste an das anfragende Gerät zurückgeben. In dieser Aufgabe wird zunächst das Serverframework programmiert, das die Dienste im Host registriert, eine „Device-Discovery“, also Suche nach
entfernten Geräten durchführt und eine Dienstanfrage beantwortet. Gefundene BT-Geräte
sollen im Serverprogramm in einer Liste ausgegeben werden. Das Handling der Clientverbindungen sei Thema der nächsten Aufgaben (C6 und C7).
Für die Programmierung soll die Java BT Implementierung von Benhui Bluecove (für Windows) sowie das Java J2SE Development Kit und die J2SE Runtime Environment 5.0 Update
10 oder höher verwendet werden. Als Entwicklungsumgebung bietet sich eclipse an. Hier
muss ein neues Java-Projekt angelegt werden. Es muss die Benhui Bluecove Bibliothek ins
Projekt eingebunden werden (über Rechte Maustaste auf Projekt -> BuildPath -> Configure
Build Path -> Add External Jars).
Das Gegenstück zu dem hier zu entwickelnden Hostprogramm ist das Clientprogramm der
Gruppe B. Sie müssen sich mit der Gruppe B absprechen, wenn Sie ihr Programm testen.
Grundstruktur eines Servers
Ein BT-Server legt für jedes Gerät, mit dem er eine Verbindung eingeht, einen Thread an.
Die Programmierschritte im Server sind:
• Initialisierung des BT-Stacks
• Antworte auf eine Dienste-Anfrage
• Warte auf Client-Verbindungsanfragen und aktzeptiere diese und bearbeite ClientAnforderungen
2
• Schließe den Dienst ab
Es existieren einige Tutorials von SUN im Netz, die recht gut sind. Im Tutorial 1 sind diese
Programmierschritte beschrieben, Tutorial 2 geht mehr auf die Programmierung im MT ein.
Zu finden sind die Tutorials unter:
•
Tutorial 1
(http://developers.sun.com/techtopics/mobility/apis/articles/bluetoothcore/)
•
Tutorial 2 (http://developers.sun.com/techtopics/mobility/midp/articles/bluetooth2/)
Außerdem zu empfehlen ist die Homepage von Benhui, auf der Beispielprogramme und viele
nützliche Informationen sowie ein Diskussionsforum zu finden sind.
Initialisierung des Servers
Um einen Dienst im Server zu registrieren, ist zunächst die Initialisierung des lokalen Bluetooth-Stacks und des lokalen BT-Gerätes nötig, d.h. der angeschlossene BT-Dongle muss über die JavaAPI initialisiert werden. Ein Aufruf von
LocalDevice localDevice = getLocalDevice();
aus dem javax.bluetooth.*-Paket erledigt dies. Zusätzlich muss ein StreamConnectionNotifier-Objekt angelegt werden, welche ähnlich wie ein Server-Socket eingehende Verbindung
akzeptiert. Die Initialisierung des StreamConnectionNotifiers geschieht über folgenden Aufruf:
StreamConnectionNotifier serverconn = (StreamConnectionNotifier)
Connector.open(url);
Die Connector-Klasse aus javax.microedition.io.Connector öffnet eine Verbindung und gibt
ein Connection- Objekt zurück, welches in die entsprechende Verbindungsart gecastet werden kann (hier also StreamConnectionNotifier). Der url-Parameterstring muss dem URLFormat aus RFC 2396 entsprechen. Er hat die allgemeine Form wie folgt:
{scheme}:[{target}][{params}]
•
{Scheme}: Name des Protkolls, z.B. btspp oder http. (Bei uns: btspp)
•
{target}: Eine Netzwerkadresse, für den lokalen Host "localhost:1", für entfernte Geräte eine BT-Adresse
•
[{params}]: Weitere Parameter der Form ";x=Y", z.B. ";app-name=HostNode"
Hinter der {target}-Netzwerkadresse wird die PSM (Protocol Service Muliplexer, wichtigster
Parameter der L2CAP-Verbindung, siehe [1]) angegeben, um einen speziellen Dienst im Server anzusprechen. PSM 1 (genauer 0x0001) entspricht z.B. dem ServiceDiscoveryProtocol,
das wir allerdings nicht verwenden. Auf diese Weise kann später mittels der UUIDs für die
Serverdienste ein Dienst gezielt ausgewählt werden (eine PSM entspricht letztlich einer
UUID, da die 16- bzw. 32-Bit-PSM intern in eine 128-Bit UUID umgewandelt wird (siehe
BluetoothAPI:UUID für eine Beschreibung der UUID-Klasse).
Obwohl wir das Service Discovery Protokoll (SDP) nicht verwenden, hier noch einige Bemerkungen dazu: Die UUIDs für die Serverdienste müssen angelegt und im ServiceRecord
des Hosts abgelegt werden. Damit sind die Services im ServiceRecord der localDevice regist-
3
riert und können bei einer ServiceDiscovery durch ein entferntes Gerät gefunden werden. Eine Beschreibung des SDP findet sich in der Bluetooth Core Specification v2.0.
Akzeptieren einer Client Verbindung
In dieser Aufgabe geht es darum, eingehende Clientverbindungen zu verarbeiten. Die Dienstauswahl geschieht hier vereinfacht über den Austausch von Strings, die im Host geparst werden. Das Mobiltelefon (MT) schickt zuerst einen String, der in der Spezifikation definiert ist,
an den Host. Im Host soll dies erkannt werden und die registrierten Dienste ebenfalls als
String zurückgeschickt werden (siehe Spezifikation). Dieser String kann vom MT wiederum
erkannt werden, welches dann die einzelnen Dienste zur Auswahl freigibt. Das Handling der
angebotenen Dienste wird in den folgenden Aufgaben durchgeführt. Hier soll zunächst nur
die Dienst-Antwort auf die Dienstanfrage des Client implementiert werden.
Akzeptieren einer Clientverbindung wird durch
StreamConnection clientconn = serverconn.acceptAndOpen();
ausgeführt. Dadurch wird ein StreamConnection Objekt erstellt, über das verschiedene
Schreib- und Leseoperationen durchgeführt werden können
Dienstanfrage: „Service Discovery“
Für die Service Discovery sendet in diesem Projekt das Mobiltelefon einen String (über das
„Serial Port Profile“ SPP) " <SendServices>" an den Server. Dieser parst den String und
schickt seinerseits einen String mit der Liste der angebotenen Dienste (also "Telefon-Service,
Gebäudeplan-Service, Quiz-Service-String") zurück. Falsche (unverständliche) Anfragen
vom Handy sollten mit einer Fehlermeldung beantwortet werden.
Benutzen Sie für Input und Output über Bluetooth die Funktionen writeUTF(String s) und
readUTF(). Sie müssen zunächst ein DataInputStream Objekt für die eingehende Anfrage erzeugen:
DataInputStream in = clientconn.openDataInputStream();,
bevor sie mit
String s = in.readUTF();
den eingehenden String lesen können. Analog läuft das Schreiben auf den DataOutputStream
ab:
DataOutputStream out = clientconn.openDataOutputStream();
out.writeUTF("ihreantwort");
Da es sich bei der geöffneten Verbindung um eine Verbindung mit Puffer handelt, müssen
Sie diesen leeren und somit die Nachricht senden:
outStream.flush();
Schließen Sie danach den OutputSream, nicht jedoch ihre Stream Connection:
outStream.close();
4
Editieren, Kompilieren und Ausführen Ihres Programms
Für das Editieren Ihres Programms wird eclipse empfohlen. Für das Kompilieren gibt es zwei
Möglichkeiten:
• Sie können ebenfalls auf eclipse mit dem Java2SE-plugin kompilieren. Dafür müssen Sie folgende Bibliothek einbinden: BlueCoveJSR82-Patched-By-Benhui-net.jar
Den Link zu dieser Bibliothek finden Sie oben.
• Die andere Möglichkeit ist das Kompilieren in der DOS-Eingabeaufforderung Ihres
Windows-Rechners. Sie können dafür ein kleines BAT-Programm anlegen wie folgt:
javac -d bin -Xlint:unchecked -cp BlueCoveJSR82-Patched-By-Benhui-net.jar src\*.java
• Gehen Sie in der Eingabeaufforderung in ihr Entwicklungsverzeichnis und geben
führen Sie die obige BAT-Datei aus.
• Für das Ausführen Ihres Programms wir ebenfalls eine kleine BAT-Datei empfohlen, wie folgt:
javac -cp BlueCoveJSR82-Patched-By-Benhui-net.jar;bin StartC8
Der folgende Coderahmen für den Server hilft Ihnen ein wenig beim Einstieg in die Codierung.
---------------------------------------------------------------------------------------------------------------import java.io.FileWriter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import javax.bluetooth.*;
import javax.microedition.io.*;
/**
* Praktikum Eingebettete Systeme WS 07/08
* Aufgabe C5
* Datum: …
* @author: Gruppe C: ...Namen
* @version: …
*
* Service discovery is done via SPP
* The Services found are displayed to StdOut
* Server waits for Client connections
*
* Handles the client connections:
* parsing the services:
* 1)Send Services
* 2)Map Service
* 3)Phone Service
*/
/* Starte den Server */
public class StartC5 {
public static void main(String[] args) {
C5Server server = new C5Server();
5
server.start();
}
}
public class C5Server extends Thread
{
private StreamConnectionNotifier serverconn; // the server side connection object
private LocalDevice localDevice;
// Das Lokale Gerät (Ihr PC mit BT-Dongle)
private FileWriter logfile;
private static Vector<RemoteDevice> devices = new Vector<RemoteDevice>();
private static Vector<DeviceClass> deviceClasses = new Vector<DeviceClass>();
public C5Server()
{ // Constructor Funktion. Eventuell nicht nötig
}
public synchronized void run()
{
try
{
// init the stack, get the local device
// ....... fügen Sie hier Ihren Init-Code ein ........
// Machen Sie Ihr eigenes Gerät allgemein sichtbar:
// ....... fügen Sie hier Ihren set discoverable -Code ein……
// mit GIAC(General/Unlimited Inquiry Access Code)-Parameter
// siehe Tutorial 1
// Empfehlung: legen Sie ein log an:
// z.B. log("localDevice set discoverable GIAC",0);
//Do the device discovery: eventuell in einer eigenen Anwendung!
//....... fügen Sie hier Ihren device Discovery-Code ein ....
// Eventuell ist es günstig, eine eigene
// "Discovery Agent (da)" Klasse zu schreiben
// diese zu instanziieren und zu starten ...run()
// Starte die Rundum-Abfrage: z.B.:
// da.startInquiry(......GIAC, new Listener());
// Implementierung eines "Discovery Listeners" Interface, der Device
// Discovery durchführt
// ....... fügen Sie hier Ihren Device-Listener-Code ein ..........
// z.B.: public class Listener implements DiscoveryListener
// Legen Sie einen Device Vektor an und greifen Sie auf diesen über eine
// Methode zu.
// Fügen Sie als Elemente die Devices in einen Device-Vektor ein
// Error handling, falls die Inquiry nicht klappt
//
// Eventuell müssen Sie hier einige Sekunden warten, bis die Device
// Discovery abgeschlossen ist
// etwa mit wait(20000);
// Schließen Sie die Inquiry
// loggen Sie die erfolgreiche D-Discovery!
// und zeigen Sie die Geräte an.
6
//
// Empfohlen: Eine ClientHandler-Klasse (runnable) anlegen, die die
// Verbindung mir dem Client (den Clients) abarbeitet
// ....... fügen Sie hier Ihren ClientHandler-Code ein ..........
// Für jeden Client wird ein ClientHandler (sozusagen als Thread)
// aufgerufen
// ClientHandler ch = new
// ClientHandler(serverconn.acceptAndOpen(), this, ...);
// Nehemen Sie die Verbindungen von entfernten Geräten an (remote devi
// ces)
// RemoteDevice rdev = RemoteDevice.getRemoteDevice(clientconn);
// clientconn.openDataInputStream();
// String s = in.readUTF();
// Wenn s = Diensteanfrage,
//
dann: this.wait(100); und antworte mit "Dienste-String"
// antwort = "Dienste-String"
// out.writeUTF(antwort);
// out.flush();
//
// Falls s = eine Dienstanforderung, dann führe den Dienst aus
//
}
}
}
----------------------------------------------------------------------------------------------------------------
Literatur
[1] Sauter, Martin: Grundkurs Mobile Kommunikationssysteme, Kapitel 5.
Herunterladen