public interface IWorker
It was then easy to write a server in the interactive F# console.
Import the interface definition
Define an implementation object. We’ll register this as a Singleton so we need the override on InitializeLifetimeService to prevent it timing out if no calls are made to it within the lease timeout interval. We print out the identity as this will be passed when we start using a secure connection later.
type MyImpl() =
inherit System.MarshalByRefObject() as base
do printfn "New instance made"
interface WorkerInterface.IWorker with
member self.DoWork () =
printfn "Method called %s " System.Threading.Thread.CurrentPrincipal.Identity.Name
override this.InitializeLifetimeService() = null
We load a small amount of configuration from a file Config1.txt containing
<channel ref="tcp" port="8080" />
We can then make an instance of our implementation object which we can register with the remoting system.
let target = new MyImpl();;
System.Runtime.Remoting.RemotingServices.Marshal (target, "target.rem");;
In another F# interactive console we can exercise this server.
let obj = System.Activator.GetObject(typeof<WorkerInterface.IWorker>, "tcp://localhost:8080/target.rem");;
let typedObj = ((box obj) :?> WorkerInterface.IWorker);;
Now, suppose we want to encrypt and sign the message as it passes across the network. We may also want to identify the caller.
On the server side, we can turn this on by simply calling
passing the second argument as true instead of false.
At this point if we re-run the same client sequence as above, the code hangs while making the call to DoWork. This is apparently because the server is now trying to do the handshake for securing the channel and the client isn’t able to respond.
The obvious fix seemed to be to
System.Runtime.Remoting.RemotingConfiguration.Configure(… some config file…, true);;
with the true parameter telling the system to make things secure. What confused me initially was that the configuratioin file didn’t contain a system.runtime.remoting element which caused the Configure call to be ignored. Using a configuration file of the form,
<channel ref="tcp" />
was successful, and the server now displays.
Method called PHILIP-J-FRYClive
The identity of the caller has been passed and made available to the server code.
Using the interactive REPL, it is easy to navigate around the remoting structures to check that the underlying stream is indeed secure.
let getPrivateField (obj : System.Object) name =
let flags = System.Reflection.BindingFlags.NonPublic ||| System.Reflection.BindingFlags.Instance
let fieldInfo = obj.GetType().GetField(name, flags)
let proxy = System.Runtime.Remoting.RemotingServices.GetRealProxy(typedObj);;
let flags = System.Reflection.BindingFlags.NonPublic ||| System.Reflection.BindingFlags.Instance ||| System.Reflection.BindingFlags.FlattenHierarchy ||| System.Reflection.BindingFlags.GetProperty;;
let identity = it.GetValue(proxy, null);;
let sink1 = it.GetValue(identity, null);;
let transport = it.GetValue(sink1, null);;
getPrivateField transport "_channel" ;;
I love the way that F# allows me to easily explore what is going on, without the need to use a debugger and gives me the ability to easily write code to aid the exploration.