Hallo zusammen,
Hier eine kleine Spielerei, die vielleicht noch nicht ganz ausgereift, aber dennoch verwendbar ist (denke ich ).
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] [EINKLAPPEN] SuperStrict
Framework brl.blitz Import "RPC.bmx"
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
Type Test Method Out(a:ABC) { RPC } Print("ABC: a -> " + a.a + ", b -> " + a.b) End Method End Type
Local bank:TBank = CreateBank() Local stream:TBankStream = CreateBankStream(bank)
Local a:ABC = New ABC a.a = 10 a.b = 33
Local myrpc:RPC = RPC.Create(stream)
myrpc.Call("Test.Out", [a])
a.a = 11 myrpc.Call("Test.Out", [a])
stream.Seek(0) myrpc.ExecuteOne() myrpc.ExecuteOne()
End
Ausgabe:
Code: [AUSKLAPPEN] [EINKLAPPEN] ABC: a -> 10, b -> 33
ABC: a -> 11, b -> 33
RPC.bmx (enthält die RPC-Klassen)
BlitzMax: [AUSKLAPPEN] [EINKLAPPEN] Import brl.stream Import brl.reflection Import brl.retro
Type RPC Field _stream:TStream
Function Create:RPC(stream:TStream) Local rpc:RPC = New RPC rpc._stream = stream Return rpc End Function
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
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
Method _LocalCall(methodName:String, params:Object[]) Local rpcMethod:TMethod = _GetRPCMethod(methodName, params.Length) Local context:Object = rpcMethod._selfTypeId.NewObject() rpcMethod.Invoke(context, params) End Method
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
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
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
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
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
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
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
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
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
|