RPC: Remote Procedure Call

Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Neue Antwort erstellen

Jolinah

Betreff: RPC: Remote Procedure Call

BeitragDo, Feb 07, 2013 18:35
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo zusammen,

Hier eine kleine Spielerei, die vielleicht noch nicht ganz ausgereift, aber dennoch verwendbar ist (denke ich Smile).

RPC lässt sich leider nicht so schön auf Deutsch übersetzen, aber im Prinzip heisst es "Entfernter Funktions-Aufruf". Und mit entfernt ist gemeint, dass man von Rechner A eine Funktion auf dem Rechner B aufrufen kann. Das bedeutet natürlich, dass hierzu etwas übers Netzwerk gesendet wird. Im Prinzip ist es also nichts anderes als eine Client-Server-Kommunikation.

Bei RPC geht es jedoch darum diese Kommunikation und die damit verbundenen Probleme für den Nutzer möglichst zu verstecken. Der Programmierer ruft dann eine Funktion genau so auf, als ob er sie auch im eigenen Programm ausführen würde. Und je nach Umsetzung des RPC kriegt er auch gleich den Rückgabewert der Funktion zurück, obwohl diese sozusagen auf Rechner B ausgeführt wurde.

Bei meiner Spielerei habe ich jedoch auf Rückgabewerte und auch auf Netzwerk verzichtet. Die RPC-Klasse kann generell mit TStream umgehen. Wer Netzwerk nutzen will, kann einfach einen Netzwerk-Stream bereitstellen.

Da in BlitzMax Int, Float etc. keine Objekte sind, habe ich jeweils Wrapper-Klassen erstellt. Diese heissen RPCInt, RPCFloat usw. und können vom RPC über die Methoden Serialize und Deserialize in den Stream serialisiert werden. Diese müssen nur verwendet werden bei RPC.Call, bei der RPC-Funktion selber sind diese nicht Notwendig. (z.B. myrpc.Call("Klasse.Methode", [RPCInt.Create(5)]) )

Hier der Code:

Main.bmx = Beispiel
BlitzMax: [AUSKLAPPEN]
SuperStrict

Framework brl.blitz
Import "RPC.bmx"

'Klasse die als Parameter der RPC-Funktion übergeben wird
'Diese muss serialisiert werden können, das wird über
'die Methoden Serialize und Deserialize erreicht
Type ABC
Field a:Int
Field b:Int

Method Serialize(stream:TStream)
stream.WriteInt(a)
stream.WriteInt(b)
End Method

Method Deserialize(stream:TStream)
a = stream.ReadInt()
b = stream.ReadInt()
End Method
End Type

'Klasse mit der RPC-Funktion "Out"
Type Test
'Die Methode wird via Metadaten als RPC-Methode gekennzeichnet: siehe { RPC }
Method Out(a:ABC) { RPC }
Print("ABC: a -> " + a.a + ", b -> " + a.b)
End Method
End Type

'Die RPC-Klasse funktioniert mit jedem Stream. Statt Netzwerk wird hier
'eine Bank zusammen mit einem BankStream verwendet
Local bank:TBank = CreateBank()
Local stream:TBankStream = CreateBankStream(bank)

'Es wird ein ABC-Objekt zum Übergeben vorbereitet
Local a:ABC = New ABC
a.a = 10
a.b = 33

'RPC-Objekt erstellen
Local myrpc:RPC = RPC.Create(stream)

'Wenn man nun die RPC-Funktion Test.Out auf einem entfernten Rechner ausführen möchte
'ruft man einfach Call("Test.Out", Parameter) auf
myrpc.Call("Test.Out", [a])

'Hier noch ein zweiter Aufruf mit modifiziertem Parameter
a.a = 11
myrpc.Call("Test.Out", [a])

'Ausgeführt werden die Funktionen natürlich erst wenn die Gegenstelle den Stream
'ausliest und pro empfangenem Aufruf je ein ExecuteOne() ausführt

stream.Seek(0) 'stream position auf den Anfang setzen zum Auslesen
myrpc.ExecuteOne() 'Erste Funktion ausführen
myrpc.ExecuteOne() 'Zweite Funktion ausführen

End


Ausgabe:
Code: [AUSKLAPPEN]
ABC: a -> 10, b -> 33
ABC: a -> 11, b -> 33


RPC.bmx (enthält die RPC-Klassen)
BlitzMax: [AUSKLAPPEN]

Import brl.stream
Import brl.reflection
Import brl.retro

Rem
bbdoc: Klasse zum Ausführung von Methoden aus der Ferne :) (Remote Procedure Call, RPC)
End Rem

Type RPC
Field _stream:TStream

Rem
bbdoc: Erstellt ein neues RPC-Objekt
End Rem

Function Create:RPC(stream:TStream)
Local rpc:RPC = New RPC
rpc._stream = stream
Return rpc
End Function

Rem
bbdoc: Führt ein RPC aus
End Rem

Method Call(methodName:String, params:Object[])
_ValidateParams(params)

_stream.WriteShort(methodName.Length)
_stream.WriteString(methodName)

If params = Null Or params.Length = 0 Then
_stream.WriteByte(0)
Else
_stream.WriteByte(params.Length)

For Local i:Int = 0 Until params.Length
Local t:TTypeId = TTypeId.ForObject(params[i])
_stream.WriteShort(t.Name().Length)
_stream.WriteString(t.Name())

If t.Name() = "String" Then
_stream.WriteShort(String(params[i]).Length)
_stream.WriteString(String(params[i]))
Else
Local m:TMethod = t.FindMethod("Serialize")
m.Invoke(params[i], [_stream])
End If
Next
End If
End Method

Rem
bbdoc: Führt eine angeforderte Funktion aus
End Rem

Method ExecuteOne()
Local l:Short = _stream.ReadShort()
Local methodName:String = _stream.ReadString(l)

Local paramCount:Byte = _stream.ReadByte()
Local params:Object[] = New Object[0]

For Local i:Int = 0 Until paramCount
l = _stream.ReadShort()
Local typeName:String = _stream.ReadString(l)

Local t:TTypeId = TTypeId.ForName(typeName)
If t = Null Then
Throw RPCException.Create("Could not find the type " + typeName)
End If

Local p:Object = Null

If t.Name() = "String" Then
l = _stream.ReadShort()
p = _stream.ReadString(l)
Else
Local m:TMethod = t.FindMethod("Deserialize")
If m = Null Or m.ArgTypes().Length <> 1 Or m.ArgTypes()[0].Name() <> "TStream" Then
Throw RPCException.Create("The type " + typeName + " is not deserializable. Please implement the method Deserialize(stream:TStream).")
End If

p = t.NewObject()
m.Invoke(p, [_stream])
End If

params = params[..params.Length + 1]
params[params.Length - 1] = p
Next

_LocalCall(methodName, params)
End Method

Rem
bbdoc: Führt eine RPC-Methode lokal aus
End Rem

Method _LocalCall(methodName:String, params:Object[])
'Find the RPC method
Local rpcMethod:TMethod = _GetRPCMethod(methodName, params.Length)

'Invoke the local RPC method
Local context:Object = rpcMethod._selfTypeId.NewObject()
rpcMethod.Invoke(context, params)
End Method

Rem
bbdoc: Sucht nach der angegebenen RPC-Methode mit der übereinstimmenden Anzahl Argumente
End Rem

Method _GetRPCMethod:TMethod(methodName:String, argCount:Int)
If methodName = Null Or methodName.Trim().Length = 0 Or Not methodName.Contains(".") Then
Throw RPCException.Create("Invalid RPC method name: " + methodName)
End If

Local index:Int = methodName.Find(".")
Local typeName:String = Mid(methodName, 1, index)
methodName = Mid(methodName, index + 2)

Local t:TTypeId = TTypeId.ForName(typeName)
If t = Null Then
Throw RPCException.Create("Invalid RPC method name: " + methodName + ", type " + typeName + " does not exist.")
End If

Local m:TMethod = t.FindMethod(methodName)
If m = Null Then
Throw RPCException.Create("Invalid RPC method name: " + methodName)
End If

If Not m.MetaData().Contains("RPC") Then
Throw RPCException.Create("Method " + methodName + " is not a RPC method. Please add metadata { RPC } at the end of the method declaration.")
End If

If m.ArgTypes().Length <> argCount Then
Throw RPCException.Create("The RPC method " + methodName + " expects a different number of arguments.")
End If

Return m
End Method

Rem
bbdoc: Überprüft alle Parameter auf unterstützte Datentypen. Ausser String muss jeder Datentyp serialisiert werden können
End Rem

Method _ValidateParams(params:Object[])
For Local i:Int = 0 Until params.Length
Local t:TTypeId = TTypeId.ForObject(params[i])
If t = Null Then
Throw RPCException.Create("The type of the parameter with index " + i + " could not be determined.")
End If

If t.Name() = "String" Then Continue

Local m:TMethod = t.FindMethod("Serialize")
If m = Null Or m.ArgTypes().Length <> 1 Or m.ArgTypes()[0].Name() <> "TStream" Then
Throw RPCException.Create("The type " + t.Name() + " is not serializable. Please implement the method Serialize(stream:TStream).")
End If

m = t.FindMethod("Deserialize")
If m = Null Or m.ArgTypes().Length <> 1 Or m.ArgTypes()[0].Name() <> "TStream" Then
Throw RPCException.Create("The type " + t.Name() + " is not deserializable. Please implement the method Deserialize(stream:TStream).")
End If
Next
End Method

End Type

Rem
bbdoc: Exceptions die von der RPC-Klasse geworfen werden
End Rem

Type RPCException
Field Message:String

Function Create:RPCException(msg:String)
Local e:RPCException = New RPCException
e.Message = msg
Return e
End Function

Method ToString:String()
Return Message
End Method
End Type

Rem
bbdoc: Wrapper für Byte
End Rem

Type RPCByte
Field value:Byte

Function Create:RPCByte(v:Byte)
Local b:RPCByte = New RPCByte
b.value = v
Return b
End Function

Method Serialize(stream:TStream)
stream.WriteByte(value)
End Method

Method Deserialize(stream:TStream)
value = stream.ReadByte()
End Method

Method ToString:String()
Return value
End Method
End Type

Rem
bbdoc: Wrapper für Short
End Rem

Type RPCShort
Field value:Short

Function Create:RPCShort(v:Short)
Local s:RPCShort = New RPCShort
s.value = v
Return s
End Function

Method Serialize(stream:TStream)
stream.WriteShort(value)
End Method

Method Deserialize(stream:TStream)
value = stream.ReadShort()
End Method

Method ToString:String()
Return value
End Method
End Type

Rem
bbdoc: Wrapper für Int
End Rem

Type RPCInt
Field value:Int

Function Create:RPCInt(v:Int)
Local i:RPCInt = New RPCInt
i.value = v
Return i
End Function

Method Serialize(stream:TStream)
stream.WriteInt(value)
End Method

Method Deserialize(stream:TStream)
value = stream.ReadInt()
End Method

Method ToString:String()
Return value
End Method
End Type

Rem
bbdoc: Wrapper für Long
End Rem

Type RPCLong
Field value:Long

Function Create:RPCLong(v:Long)
Local l:RPCLong = New RPCLong
l.value = v
Return l
End Function

Method Serialize(stream:TStream)
stream.WriteLong(value)
End Method

Method Deserialize(stream:TStream)
value = stream.ReadLong()
End Method

Method ToString:String()
Return value
End Method
End Type

Rem
bbdoc: Wrapper für Float
End Rem

Type RPCFloat
Field value:Float

Function Create:RPCFloat(v:Float)
Local f:RPCFloat = New RPCFloat
f.value = v
Return f
End Function

Method Serialize(stream:TStream)
stream.WriteFloat(value)
End Method

Method Deserialize(stream:TStream)
value = stream.ReadFloat()
End Method

Method ToString:String()
Return value
End Method
End Type

Rem
bbdoc: Wrapper für Double
End Rem

Type RPCDouble
Field value:Float

Function Create:RPCDouble(v:Double)
Local d:RPCDouble = New RPCDouble
d.value = v
Return d
End Function

Method Serialize(stream:TStream)
stream.WriteDouble(value)
End Method

Method Deserialize(stream:TStream)
value = stream.ReadDouble()
End Method

Method ToString:String()
Return value
End Method
End Type

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group