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