Thread .NET API einbinden
(43 answers)
Opened by bianca at 2010-03-02 12:18
Also auf bitten von bianca will ich zeigen wie man eine Anbindung an C# hin bekommen kann. Es ist nicht beste Weg, oder der sicherste, aber dafür ist er einfach, da man auf normierte Schnittstellen zurückgreifen kann.
Alles schritt für Schritt (sofern ich nichts vergessen habe) 1. Ich habe den C# Teil mit Monodevelop erstellt. Ich habe aber nur eine momo-spezifische Bibliothek benutzt (Mono::GetOptions). Ich hatte einfach keine Lust nur zum Parsen der Kommandozeilenoptionen noch eine neue Lib an zu schauen. 2. Ich nutze XML-RPC um die Daten zwischen Perl und C# zu transferieren. Auf der C#-Seite ist das CookComputing.XmlRpc; und auf der perl-Seite RPC::XML::Client. 3. Wenn kein Service (C#) läuft wird er vom Client gestartet und verwaltet. Das macht es möglich den Service auch getrennt zu starten und mehrere Clients darauf zugreifen zu lassen. 4. Ich habe nicht viel Übung in C# und dementsprechend sieht der Code auch aus. :-) Zunächst der Service in C#. Zunächst habe ich ein neues Projekt angelegt: Datei->Neu->Projektmappe... Ein Fenster geht Auf in dem man um Feld Template C#->Terminal Projekt auswählt. Nachdem man einen Projektnamen eingeben hat. (Ich wählte "xml_rpc_deamon") wird das Projekt erstellt. Man bekommt eine Datei "Main.cs" in die man folgendes eintragen sollte: Code: (dl
)
1 using System; Die Zeile namespace xml_rpc_deamon sollte man entsprechend Anpassen. Will man das nun ausführen mosert der Compiler, dass er einige Libs nicht finden kann. Das Problem löst man indem man im Menu Projekt->Referenzen bearbeiten... auswählt. Ein Fenster öffnen sich und kann die Pakete System.Runtime.Remoting und Mono.GetOptions zusätzlich auswählen. Für CookComputing.XmlRpc muss man erst die das Paket von http://www.xml-rpc.net/ herunter laden und entpaken. Dann wählt man im selben Fenster den Reitereintrag .Net-Assembly und selektiert dort im Entpakten Ordner bin/CookComputing.XmlRpcV2.dll und fügt es der Liste hinzu. Nun sollte sich das Programm kompilieren und starten lassen. Im Projektordner findet man unter <projektname>/<namespace>/bin/Debug/ eine "dll" und eine "exe" die "exe" Lässt sich mit "mono <name>.exe" starten. Das ist der kompilierte Code von oben. Bei mit sieht das so aus: Code: (dl
)
1 #>ls -l ~/Projects/xml_rpc_deamon/xml_rpc_deamon/bin/Debug/ Halbzeit ist erreicht, der Service in C# steht und sollte sich starten lassen. Nun kommen wir zum Perl Teil: Ich habe die exe und dll in einen Ordner "mono" kopiert und dazu ein script "mono_perl_ipc.pl" Code: (dl
)
1 -rwxr--r-- 1 topeg topeg 5254 9. Apr 00:22 mono_perl_ipc.pl Der Inhalt des Scriptes: Ich hoffe der Code ist einigermaßen verständlich. Code (perl): (dl
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 #!/usr/bin/perl use strict; use warnings; my $port="5678"; my $wait=2; my $service=csharp_ipc_service->new($port,$wait); print csharp_ipc_service::error()."\n" unless($service); # Etwas Demonstartion, dass es Funktioniert if($service->test_running()) { print "test_running() erfolgreich\n"; } else { print "ERROR:".$service->error()."\n"; } print "#"x80,"\n"; if($service->test_running()) { print "test_running() erfolgreich\n"; } else { print "ERROR:".$service->error()."\n"; } print "#"x80,"\n"; my $val=$service->test_get(1); if(defined($val)) { print "test_get(1) = $val\n"; } else { print "ERROR:".$service->error()."\n"; } print "#"x80,"\n"; $val=$service->test_get(4); if(defined($val)) { print "test_get(4) = $val\n"; } else { print "ERROR:".$service->error()."\n"; } print "#"x80,"\n"; ######################################################################## ######################################################################## ######################################################################## {package csharp_ipc_service; use strict; use warnings; use RPC::XML; use RPC::XML::Client; use FindBin; use POSIX ":sys_wait_h"; my $ERROR=undef; #----------------------------------------------------------------------- sub new { my $class=shift; my $port=shift; my $wait=shift; my $self={}; $self->{stop}=0; $self->{pid}=0; bless($self, $class); unless($self->_start($port,$wait)) { $ERROR=$self->{ERROR}; return undef; } return $self; } #----------------------------------------------------------------------- # Kommandos, die auf dem Server ausgeführt werden sollen sub test_running { my $self=shift; my $ret=$self->_runn_cmd('test.Running'); return 1 if(defined($ret)); return 0; } sub test_get { my $self=shift; my $number=shift; return $self->_runn_cmd('test.Get',$number); } #----------------------------------------------------------------------- # Fehler ausgeben wenn sie auftreten sub error { my $self=shift; if($self && ref($self) eq __PACKAGE__) { my $err=$self->{ERROR} || ''; $self->{ERROR}=undef if($self->{ERROR}); return $err; } else { my $err=$ERROR; $ERROR=undef; return $err; } } ######################################################################## # privat # ######################################################################## sub _add_error { my $self=shift; my $msg=shift; if($msg) { if($self->{ERROR}) { $self->{ERROR}.="\n$msg"; } else { $self->{ERROR}=$msg; } } } sub _runn_cmd { my $self=shift; my $resp=$self->{ipc}->send_request(@_); if(ref($resp) && ref($resp) ne 'RPC::XML::fault') { return $resp->value(); } else { if(ref($resp)) { $self->_add_error($resp->string()); } else { $self->_add_error("no server connection ($!)"); } } return undef; } sub _sig_child { my $self=shift; my $msg=waitpid($self->{pid},0); $self->_add_error("server died unexpected") unless($self->{stop}); } sub _start { my $self=shift; my $port=shift || 5678; my $wait=shift || 5; unless($self->{pid}) { $self->{stop}=0; $self->{pid}=0; $self->{ipc}=undef; local $SIG{CHLD}=sub{ $self->_sig_child(@_); }; # XML-IPC Client initialisieren my $host='http://localhost:'.$port.'/test'; $self->{ipc}=RPC::XML::Client->new($host); # läuft möglicherweise schon ein Service? # wenn ja, keinen eigenen starten. my $resp=$self->{ipc}->send_request('x'); unless(ref($resp)) { #Service starten my $cs_pid=fork(); if(defined($cs_pid)) { if($cs_pid) { $self->{pid}=$cs_pid; # warten dass der Server hochkommt. sleep($wait); } # im Kindprozess C# Programm starten # da müsste man noch etwas machen wenn man auch # den MS-Interpreter nutzen möchte # zudem ist das alles auf linux/Unix abgestimmt # (siehe Pfadangabe) else { # programm starten exec("/usr/bin/mono $FindBin::Bin/mono/xml_rpc_deamon.exe -d -s localhost -p $port"); # wenn man hier ankommt lief was verkehrt! exit(10); } } else # fork hat nicht geklappt! { $self->_add_error("Fork failed"); return 0; } } return 1; } } sub _stop { my $self=shift; if($self->{pid}) { $self->{stop}=1; my $pid=$self->{pid}; local $SIG{CHLD}='DEFAULT'; # zu beenden auffordern kill('KILL',$pid) if(waitpid($pid, WNOHANG)>-1); # maximal 20 Sekunden warten. eval{ local $SIG{ALRM}={die("timeout1\n")}; alarm(20); waitpid($pid,0); alarm(0); }; # 20 Sekunden gewartet ohne dass der Prozess beendet wurde if($@ && waitpid(-1, WNOHANG)>-1) { # Abwürgen kill('TERM',$pid) if(waitpid($pid, WNOHANG)>-1); # und 2 Sekunden warten eval{ local $SIG{ALRM}={die("timeout2\n")}; alarm(5); waitpid($pid,0); alarm(0); }; # Prozess hängt ganz übel # Deadlock ?? if($@ && waitpid(-1, WNOHANG)>-1) { $SIG{CHLD}='IGNORE'; die("Can't kill $pid!\n"); } } } } sub DESTROY { _stop(@_); } 1;} sollte alles laufen bekommt man eine Ausgabe wie diese: Code: (dl
)
1 ./mono_perl_ipc.pl Ich habe hier nur das Übertragen von Strings und Integer gezeigt, aber XML-RPC kann auch mit Arrays und Hashes umgehen, wenn man noch komplexere Datenstrukturen übertragen will kann man tiefer in die XML-RPC Kommunikation Einsteigen oder die Daten Serialisieren. Wenn gewünscht, kann ich noch später noch etwas genauer auf den bestimmte Abschnitte eingehen. EDIT: Immer diese Schreibfehler... modedit Editiert von pq: more-tags hinzugefügt Last edited: 2012-02-11 16:03:32 +0100 (CET) |