Adaptive
Communication
Environment
Teil 1
Gliederung
Motivation
Überblick ACE
Entwurfsmuster,
Framework
Wrapper Facade
Architektur ACE
Bsp. Interprozesskommunikation mit ACE
Motivation
Gängige Anforderungen an Software
Erweiterbar
Zuverlässig
Flexibel
Wiederverwendbar
Portierbar
Finanzierbar
Schwer realisierbar, falls
Kernkonzepte
& Softwarekomponenten
ständig neu “erfunden”
Monolithischer Softwareaufbau
Motivation
Entwicklung von Multi PlattformAnwendungen problematisch:
Komponentenbibliotheken
mit verschiedenen
API‘s und Semantik auf unterschiedlichen
Plattformen
Verschiedene Standards: z.B. POSIX,
UNIX98, Win32
Beispiel Sockets API
Fehleranfällig:
Häufig Return-Werte ignoriert
Netzwerk Byte-Ordering
vergessen
Nicht portabel:
Headerfiles
Unterschiedliche Standards
In C geschrieben
→ C++ Wrapper wünschenswert
#include <sys/types.h>
#include <sys/socket.h>
int echo_server () {
…
int n_handle;
int s_handle = socket (PFJJNIX, SOCK_DGRAM, 0);
…
addr.sin_family = AF_INET;
addr.sin_port = PORT_NUM;
…
if (bind (s_handle, (struct sockaddr *) &addr,
sizeof addr) == -1)
return -1;
if (n_handle = accept (s_handle, (struct sockaddr *) &addr,
&addr_len) != -1) {
int n;
while ((n = read (s_handle, buf, sizeof buf}) > 0)
write (n_handle, buf, n);
close (n_handle); }
return 0;
}
Überblick ACE
Open Source Software Toolkit
OO-Framework zur Entwicklung von
Kommunikationssystemen auf verschiedenen
Plattformen
Abgedeckte Aspekte:
Verbindungsaufbau und Diensteinitialisierung
Event demultiplexing & Event handler dispatching
Statische und dynamische Dienstekonfiguration
Nebenläufigkeit und Synchronisierung
Verteilte Kommunikationsdienste (Namen, Zeit,
Loggen, ...)
Überblick ACE
Entwickelt u.a. von Douglas Schmidt
1992 als open source Projekt veröffentlicht
Ab 1994 Einsatz in kommerziellen Produkten
Boeing
Siemens
Ericson
Nokia
http://www.cs.wustl.edu/~schmidt/ACE-users.html
Firma: Riverace (www.riverace.com)
Überblick ACE
Unterstützte Plattformen
Alle
Windows Versionen
Unix, Linux (SunOS 4.x, HP UX, Linux Redhat,
Suse etc.)
Echtzeit Betriebsysteme (VxWorks, Chorus ,...)
Diverse Großrechner Architekturen
Auf vielen c++ Compilern getestet
ACE - Musterübersicht
ACE implementiert
komplexe
Entwurfsmuster
und Frameworks
Exemplarisch
Double
Checked
Pattern im Kontext
“Singleton Pattern”
Bsp. Singleton
Zweck
Existenz max. einer Instanz einer Klasse
Globaler Zugriffspunkt auf Instanz
Struktur
Singleton
static instance() ○
getData()
static instance_
data_
return instance_
Bsp. Singleton
Eigene Implementierung, Versuch 1
1
2
3
4
5
6
7
public class Singleton{
private static instance_
= new Singleton();
public static instance(){
return instance_;
}
}
Probleme: impliziter Konstruktor, Instanz wird
manchmal unnötig erzeugt
Bsp. Singleton
Eigene Implementierung, Versuch 2
1
2
public class Singleton{
private static instance_;
3
private Singleton(){};
4
5
6
public static instance(){
if (instance_ == 0)
instance_ = new Singleton();
7
8
9
return instance_;
}
}
Problem: fehlende Synchronisierung in Zeilen 5-6
Bsp. Singleton
Eigene Implementierung, Versuch 3
1
public class Singleton{
... // Korrekt
2
3
4
5
6
public static instance(){
if (instance_ == 0)
synchronized {
instance_ = new Singleton();
}
7
8
9
return instance_;
}
}
Problem: falsche Synchronisierung
Bsp. Singleton
Korrekte Lösung dokumentiert im “Double
Check Pattern”
1
2
3
4
5
6
if (instance_ == 0){
synchronized {
if (instance_ == 0)
instance_ = new Singleton();
}
}
Muster
Jedes Muster beschreibt ein in unserer Umwelt
beständig wiederkehrendes Problem und erläutert den
Kern der Lösung für dieses Problem, so dass Sie diese
Lösung beliebig oft anwenden können, ohne sie jemals
ein zweites Mal gleich auszuführen
(Christopher Alexander)
Formuliert für architektonische Probleme
Idee übertragbar auf Entwurf von Softwaresystemen
Muster
Unterschiedliche Ebenen von Mustern:
Architekturmuster beschreibt Organisationsstruktur
(Subsysteme, Komponenten)
Entwurfsmuster beschreibt Struktur und Beziehungen
innerhalb eines Subsystems
Idiom beschreibt Lösung eines Implementierungsproblems in
einer bestimmten Programmiersprache
Entwurfsmuster
Beschreiben abstrakt & strukturiert oft
vorkommende Probleme & ihre Lösungen
Erfassen Entwurfsentscheidungen, die
erfahrungsgemäß gut funktionieren
Müssen an konkrete Probleme angepasst
werden
Weitgehend unabhängig von spezifischen
Programmiersprachen
Formen Sprache, um Entwürfe zu diskutieren
Entwurfsmuster - Bestandteile
Name und Klassifikation
Zweck
Auch bekannt als
Motivation
Anwendbarkeit
Struktur
Teilnehmer
Entwurfsmuster - Bestandteile
Zusammenarbeit
Konsequenzen
Implementierung
Beispielcode
Bekannte Anwendungen
Verwandte Muster
Entwurfsmuster - Vorteile
Zuverlässig
Wiederverwendbar
Erweiterbar
Vereinfachen Kommunikation
(Standardisierung von Namen…)
Framework
Implementation von Entwurfsmustern
Repräsentiert durch Menge von abstrakten Klassen und
Art und Weise wie Instanzen zusammenarbeiten
Wiederverwendbares Design für Anwendung
Definiert grundlegende Struktur und Architektur der
Anwendung
Unterschied zu Klassenbibliothek:
Hauptprogramm für globale Steuerung
Inversion of Control
Portabler Code
Möglichkeiten:
#ifdefs
#if defined (_WIN32)
SOCKET h;
#else
int h;
#endif
→komplex, schwer wartbar/erweiterbar
Musterbasiert:
Wrapper Facade →
Verschleiern von plattformspezifischem Code
Wrapper Facade
Besteht aus einer oder mehreren Klassen, die Funktionen und
Datenstrukturen (die z.B. von nichtobjektorientierten API‘s
bereitgestellt werden) innerhalb portabler, objektorientierter
Interfaces kapseln
Wrapper Facade – Implementation
Semantisch zusammengehörende Funktionen
identifizieren und in Wrapper Facade Klassen
gruppieren
Mehrere Funktionen in einer Methode vereinigen
Socket API:
ACE_SOCK_Acceptor:
…
int n_handle;
s_handle=socket(…);
…
bind(…);
listen(…);
n_handle=accept(s_handle,…);
ACE_SOCK_Acceptor acceptor;
…
acceptor.open (…);
acceptor.accept(…);
Wrapper Facade – Implementation
Funktionen unter Wrapper Facade vereinigen
um damit Portabilität zu gewährleisten
Bsp. Thread Management
Win32: CreateThread()
POSIX: pthread_create()
UI:
thr_create()
Unterschiedliche Rückgabewerte
ACE_Thread_Manager::spawn()
Benötigte Informationen über Argumente
übergeben
1 Rückgabekonvention: 0 erfolgreich, -1
Fehler (Fehlergrund in errno gespeichert)
Wrapper Facade ACE_Thread_Manager::spawn()
1.
ACE_Thread_Manager::spawn (run_svc, thread_args, THR DETACHED 1 THR
SCOPE SYSTEM);
3.
//zugrundeliegende Plattform, hier: UI Threads
thr_create (0, 0, run_svc, thread_args, THRJ3ETACHED | THR_SCOPE_SYSTBM, &thread_id);
5.
run_svc (thread_args) {/*...*/}
1. Betriebssystem erzeugt Thread Ausführungskontext
2. Betriebssystem allokiert Speicher für Thread Stack
3. Register Set des neuen Thread wird vorbereitet, ruft bei
Ausführung run_svc() auf
4. Thread als ausführbar markiert, Betriebssystem kann ihn
ausführen
ACE Architektur
ACE Architektur
Adaption Layer:
Ca.
10 % von ACE
Zwischen eigentlichen Betriebssystem API‘s und Rest
von ACE
Kapselt grundlegende OS Mechanismen mittels
Wrapper Facade → einheitliches Interface für
betriebsystemnahe Aufrufe
Implementiert in ACE_OS Klasse (ca. 500 statische C
Methoden)
ACE Architektur - Adaption Layer
Kapselt u.a. folgende OS Mechanismen:
Multi-threading
und Synchronization
Interprozesskommunikation
Event demultiplexing
Dynamisches Linking
Memory Mapping und shared memory
ACE Architektur
OO-Wrapper Layer
Ca.
50 %
Verwendet Wrapper Facade Pattern
Bietet praktisch selbe Funktionalität wie Adaption
Layer, verwendet allerdings objektorientierte,
typsichere C++ Klassen, die ACE_OS Methoden
aufrufen
Applikationen verwenden Wrapper Klassen durch
Vererbung oder Instantiierung
ACE Architektur - OO-Wrapper Layer
Nebenläufigkeit und Synchronisation
ACE_Mutex, ACE_Token, …
Prozess-/Threadmanagement
ACE_Process, ACE_Thread_Manager, …
Interprozesskommunikation
IPC SAP
Memory Management
ACE_Allocator, ACE_Malloc, …
Timer Klassen
ACE_Timer_Queue, …
Signal Handling
ACE_Sig_Handler, …
ACE Architektur
Framework Layer
Highest
level Building Blocks in ACE
Integriert und vergrößert Wrapper Klassen
Framework Komponenten basieren auf
Entwurfsmustern spezifisch für
Kommunikationssoftware
ACE Architektur – Framework Layer
Event handling framework
Reactor/Proactor
überwachen mehrere Event-Quellen
Verbindungs- und Serviceinitialisierung
Acceptor/Connector
passiver/aktiver Verbindungsaufbau, Svc_Handler verarbeitet
eigentliche Ereignisse
Stream Framework
Service Configuration Framework
Service Configurator
unterstützt dynamische Initialisierung, Unterbrechung,
Wiederaufnahme, Rekonfiguration und Beendigung von Diensten
ACE Architektur
Service and Components Layer
Fertige
Anwendungen, die auf ACE basieren,
u.a.:
Distributed logging service
Webserver JAWS
Corba-Implementierung TAO
Beispiel: IPC_SAP
ACE_IPC_SAP
ACE_SOCK
…
ACE_TLI
…
ACE_SPIPE
…
ACE_FIFO
…
ACE_SOCK - Wrapper für BSD Sockets
ACE_SOCK_Acceptor
passiver Verbindungsaufbau, basierend auf accept() und listen()
ACE_SOCK_Connector
aktiver Verbindungsaufbau, basierend auf connect()
ACE_SOCK_Dgram
kapselt UDP Protokoll-Funktionalität
ACE_SOCK_IO
kapselt u.a. send(), receive(), write()
ACE_SOCK_Stream
kapselt TCP Protokoll-Funktionalität, nach Verbindungsaufbau verwendet
Beispiel: passiver Server
#include "ace/SOCK_Acceptor.h"
#include "ace/SOCK_Stream.h"
…
class Server{
public:
Server (int port):
server_addr_(port),peer_acceptor_(server_addr_) {
data_buf_= new char[SIZE_BUF]; }
int accept_connections ()
{
if (peer_acceptor_.get_local_addr (server_addr_) == -1)
ACE_ERROR_RETURN ((LM_ERROR,"%p\n","Error in get_local_addr"),1);
ACE_DEBUG ((LM_DEBUG,"Starting server at port %d\n",
server_addr_.get_port_number ()));
while(1){
ACE_Time_Value timeout (ACE_DEFAULT_TIMEOUT);
if (peer_acceptor_.accept (new_stream_, &client_addr_, &timeout)== -1)
{
ACE_ERROR ((LM_ERROR, "%p\n", "accept"));
continue; }
else {
ACE_DEBUG((LM_DEBUG,
"Connection established with remote %s:%d\n",
client_addr_.get_host_name(),client_addr_.get_port_number()))
handle_connection(); }
}}
int handle_connection()
{
for(int i=0;i<NO_ITERATIONS;i++) {
int byte_count=0;
if( (byte_count=new_stream_.recv_n (data_buf_, SIZE_DATA, 0))==-1)
ACE_ERROR ((LM_ERROR, "%p\n", "Error in recv"));
else{
data_buf_[byte_count]=0;
ACE_DEBUG((LM_DEBUG,"Server received %s \n",data_buf_));
} }
if (new_stream_.close () == -1)
ACE_ERROR ((LM_ERROR, "%p\n", "close"));
return 0; }
private:
char *data_buf_;
ACE_INET_Addr server_addr_;
ACE_INET_Addr client_addr_;
ACE_SOCK_Acceptor peer_acceptor_;
ACE_SOCK_Stream new_stream_;
};
int main (int argc, char *argv[])
{
if(argc<2){
ACE_ERROR((LM_ERROR,"Usage %s <port_num>", argv[0]));
ACE_OS::exit(1); }
Server server(ACE_OS::atoi(argv[1]));
server.accept_connections(); }
Beispiel: Connector in Verbindung mit Acceptor
#include "ace/SOCK_Connector.h"
#include "ace/INET_Addr.h"
…
class Client{
public:
Client(char *hostname, int port):remote_addr_(port,hostname)
{
data_buf_="Hello from Client";
}
int connect_to_server()
{
ACE_DEBUG ((LM_DEBUG, "(%P|%t) Starting connect to %s:%d\n",
remote_addr_.get_host_name(),remote_addr_.get_port_number()));
if (connector_.connect (client_stream_, remote_addr_) == -1)
ACE_ERROR_RETURN ((LM_ERROR,"(%P|%t) %p\n","connection failed"),-1);
else
ACE_DEBUG ((LM_DEBUG,"(%P|%t) connected to %s\n",
remote_addr_.get_host_name ()));
return 0;
}
int send_to_server()
{
for(int i=0;i<NO_ITERATIONS; i++){
if (client_stream_.send_n (data_buf_, ACE_OS::strlen(data_buf_)+1, 0) == -1){
ACE_ERROR_RETURN ((LM_ERROR,"(%P|%t) %p\n","send_n"),0);
break;
}}
close();
}
int close()
{
if (client_stream_.close () == -1)
ACE_ERROR_RETURN ((LM_ERROR,"(%P|%t) %p\n","close"),-1);
else
return 0;
}
private:
ACE_SOCK_Stream client_stream_;
ACE_INET_Addr remote_addr_;
ACE_SOCK_Connector connector_;
char *data_buf_;
};
int main (int argc, char *argv[])
{
...
Client client(argv[1],ACE_OS::atoi(argv[2]));
client.connect_to_server();
client.send_to_server(); }
Fazit
+ plattformunabhängig
+ vielseitig anwendbar
+ gut dokumentiert
Huston, Johnson, Syyid: The ACE Programmers Guide
Schmidt, Huston: C++ Network Programming. Volume 1+2
http://www.cs.wustl.edu/adaptive.html
- Keine Exceptions, nur Fehlercodes
1992 beinhaltete C++ Standard Exceptions noch nicht hinreichend
Exceptions unter Performance Gesichtspunkten eher nachteilig