view Remoting/RemotingManager.cs @ 45:8df7f4dc5615

HTTP Server: enable KeepAlive option on TCP sockets
author Ivo Smits <Ivo@UCIS.nl>
date Wed, 12 Jun 2013 23:20:21 +0200
parents fc3eb8e49ea6
children 5e717aac4c1d
line wrap: on
line source

???using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Serialization;
using System.Threading;
using UCIS.Util;
using SysThreadPool = System.Threading.ThreadPool;

//[assembly: InternalsVisibleToAttribute("UCIS.Remoting.Proxies")]

namespace UCIS.Remoting {
	public class RemotingManager {
		Dictionary<UInt32, PendingRemoteCall> pendingCalls = new Dictionary<uint, PendingRemoteCall>();
		Dictionary<Thread, UInt32> waitingCallThreads = new Dictionary<Thread, UInt32>();
		Boolean Closed = false;

		IDictionary<String, Object> incomingCallContext = new Dictionary<String, Object>();
		[ThreadStatic]
		static IDictionary<String, Object> currentCallContext;

		public event Action<String> OnDebugLog;
		public event Action<Exception> OnErrorLog;

		private void DebugLog(String text, params Object[] args) {
			if (OnDebugLog != null) OnDebugLog(String.Format(text, args));
		}
		private void ErrorLog(Exception ex) {
			if (OnErrorLog != null) OnErrorLog(ex);
		}

		public Object LocalRoot { get; set; }

		public IDictionary<String, Object> CallContext { get { return incomingCallContext; } }
		public static IDictionary<String, Object> CurrentCallContext { get { return currentCallContext; } }

		public RemotingManager(PacketStream stream) : this(stream, null) { }
		public RemotingManager(PacketStream stream, Object localRoot) {
			this.stream = stream;
			this.LocalRoot = localRoot;
			stream.BeginReadPacketFast(ReceiveCallback, null);
		}

		#region I/O, multiplexing, encoding
		PacketStream stream;
		public PacketStream Stream { get { return stream; } }
		int streamIndex = 0;
		Dictionary<int, StreamChannel> streamChannels = new Dictionary<int, StreamChannel>();

		class StreamChannel : QueuedPacketStream {
			public RemotingManager Manager { get; private set; }
			public int StreamID { get; private set; }
			public StreamChannel OtherSide { get; set; }

			public StreamChannel(RemotingManager conn, int sid) {
				this.Manager = conn;
				this.StreamID = sid;
			}

			internal void AddBuffer(Byte[] buffer, int offset, int count) {
				if (Closed) return;
				base.AddReadBufferCopy(buffer, offset, count);
			}

			public override bool CanRead { get { return !Closed; } }
			public override bool CanWrite { get { return !Closed; } }

			public override void Flush() { }

			public override void Write(byte[] buffer, int offset, int count) {
				if (Closed) throw new ObjectDisposedException("BaseStream", "The connection has been closed");
				Manager.WriteStreamChannelPacket(StreamID ^ 0x4000, buffer, offset, count);
			}

			public override void Close() {
				StreamChannel other = OtherSide;
				MultiplexorClosed();
				if (other != null) other.CloseInternal();
				lock (Manager.streamChannels) Manager.streamChannels.Remove(StreamID);
			}
			public void CloseInternal() {
				MultiplexorClosed();
				lock (Manager.streamChannels) Manager.streamChannels.Remove(StreamID);
			}
			internal void MultiplexorClosed() {
				base.Close();
				OtherSide = null;
			}
		}

		private void ReceiveCallback(IAsyncResult ar) {
			if (ar.CompletedSynchronously) {
				SysThreadPool.QueueUserWorkItem(ReceiveCallbackA, ar);
			} else {
				ReceiveCallbackB(ar);
			}
		}
		private void ReceiveCallbackA(Object state) {
			ReceiveCallbackB((IAsyncResult)state);
		}
		private void ReceiveCallbackB(IAsyncResult ar) {
			try {
				ArraySegment<Byte> packet = stream.EndReadPacketFast(ar);
				Byte[] array = packet.Array;
				if (packet.Count < 2) throw new ArgumentOutOfRangeException("packet.Count", "Packet is too small");
				int offset = packet.Offset;
				int sid = (array[offset + 0] << 8) | (array[offset + 1] << 0);
				if ((sid & 0x8000) != 0) {
					StreamChannel substr;
					if (streamChannels.TryGetValue(sid, out substr)) substr.AddBuffer(array, offset + 2, packet.Count - 2);
				}
				stream.BeginReadPacketFast(ReceiveCallback, null);
				if (sid == 0) {
					Object obj;
					using (MemoryStream ms = new MemoryStream(packet.Array, packet.Offset + 2, packet.Count - 2, false)) obj = Deserialize(ms);
					ReceiveObject(obj);
				}
			} catch (Exception ex) {
				Closed = true;
				stream.Close();
				lock (ObjectReferencesByID) {
					ObjectReferencesByID.Clear();
					RemoteObjectReferences.Clear();
				}
				lock (pendingCalls) {
					foreach (PendingRemoteCall call in pendingCalls.Values) call.SetError(new InvalidOperationException("The connection has been closed"));
					pendingCalls.Clear();
				}
				lock (streamChannels) {
					foreach (StreamChannel s in streamChannels.Values) s.MultiplexorClosed();
					streamChannels.Clear();
				}
				ErrorLog(ex);
			}
		}
		private void SendObject(Object obj) {
			if (Closed) throw new ObjectDisposedException("RemotingManager", "The connection has been closed");
			using (MemoryStream ms = new MemoryStream()) {
				ms.WriteByte(0);
				ms.WriteByte(0);
				Serialize(ms, obj);
				lock (stream) ms.WriteTo(stream);
			}
		}

		private void WriteStreamChannelPacket(int sid, Byte[] buffer, int offset, int count) {
			Byte[] store = new Byte[count + 2];
			store[0] = (Byte)(sid >> 8);
			store[1] = (Byte)(sid >> 0);
			Buffer.BlockCopy(buffer, offset, store, 2, count);
			lock (stream) stream.Write(store, 0, store.Length);
		}

		public PacketStream GetStreamPair(out PacketStream remote) {
			StreamChannel stream;
			int sid;
			lock (streamChannels) {
				if (Closed) throw new ObjectDisposedException("BaseStream", "Reading from the base stream failed");
				while (true) {
					sid = Interlocked.Increment(ref streamIndex);
					if ((sid & 0xc000) != 0) streamIndex = sid = 0;
					sid |= 0x8000;
					if (!streamChannels.ContainsKey(sid)) break;
				}
				stream = new StreamChannel(this, sid);
				streamChannels.Add(sid, stream);
			}
			stream.OtherSide = (StreamChannel)SyncCall(new CreateStreamRequest() { StreamObject = stream, StreamID = sid | 0x4000 });
			remote = stream.OtherSide;
			return stream;
		}
		#endregion

		#region Incoming call processing
		private void ReceiveObject(Object obj) {
			if (obj is ReferenceReleaseRequest) {
				ReferenceReleaseRequest req = (ReferenceReleaseRequest)obj;
				lock (ObjectReferencesByID) {
					RemoteObjectReference objref = (RemoteObjectReference)ObjectReferencesByID[req.ObjectID];
					if (objref.Release(req.ReferenceCount) == 0) {
						ObjectReferencesByID.Remove(objref.ID);
						RemoteObjectReferences.Remove(objref);
						DebugLog("Release remoted object {0} with reference count {1}; {2} objects referenced", objref.ID, req.ReferenceCount, RemoteObjectReferences.Count);
					}
				}
			} else if (obj is SynchronousCall) {
				SynchronousCall sc = (SynchronousCall)obj;
				if (sc.IsResult) {
					PendingRemoteCall call;
					lock (pendingCalls) {
						call = pendingCalls[sc.CallID];
						pendingCalls.Remove(call.ID);
					}
					call.SetResult(sc.Data);
				} else if (sc.HasPreviousCallID) {
					PendingRemoteCall call;
					lock (pendingCalls) call = pendingCalls[sc.PreviousCallID];
					if (!call.SetNextCall(sc)) {
						ProcessRemoteCallRequest(sc);
					}
				} else {
					ProcessRemoteCallRequest(sc);
				}
			} else {
				throw new InvalidDataException("Unexpected object type");
			}
		}

		private void ProcessRemoteCallRequest(SynchronousCall call) {
			UInt32 prevcallid;
			Boolean hasprevcallid;
			Thread currentThread = Thread.CurrentThread;
			lock (waitingCallThreads) {
				hasprevcallid = waitingCallThreads.TryGetValue(currentThread, out prevcallid);
				waitingCallThreads[currentThread] = call.CallID;
			}
			IDictionary<String, Object> prevCallContext = currentCallContext;
			currentCallContext = incomingCallContext;
			try {
				call.Data = ProcessRemoteCallRequestA(call.Data);
			} finally {
				currentCallContext = prevCallContext;
				lock (waitingCallThreads) {
					if (hasprevcallid) waitingCallThreads[currentThread] = prevcallid;
					else waitingCallThreads.Remove(currentThread);
				}
			}
			call.IsResult = true;
			SendObject(call);
		}

		private Object FixReturnType(Object value, Type expectedType) {
			if (value == null) return value;
			Type valueType = value.GetType();
			if (valueType != expectedType) {
				if (valueType.IsArray) {
					Array retarray = value as Array;
					if (retarray != null) {
						if (!valueType.GetElementType().IsPublic && retarray.Rank == 1 &&
								(
									expectedType.IsArray ||
									(expectedType.IsGenericType && (expectedType.GetGenericTypeDefinition() == typeof(IEnumerable<>) || expectedType.GetGenericTypeDefinition() == typeof(ICollection<>) || expectedType.GetGenericTypeDefinition() == typeof(IList<>)))
								)
							) {
							Type btype = expectedType.IsArray ? expectedType.GetElementType() : expectedType.GetGenericArguments()[0];
							Array r = Array.CreateInstance(btype, retarray.Length);
							retarray.CopyTo(r, 0);
							value = r;
						}
					}
				}
			}
			return value;
		}
		private Object ProcessRemoteMethodCallRequestA(Object ret) {
			if (ret is DelegateCallRequest) {
				DelegateCallRequest call = (DelegateCallRequest)ret;
				Object target = call.Delegate;
				DebugLog("Remote delegate call on {0}", target);
				if (ReferenceEquals(target, null)) throw new NullReferenceException("target");
				if (!(target is Delegate)) throw new InvalidCastException("target");
				Object[] args = call.Arguments;
				return ((Delegate)target).DynamicInvoke(args);
			} else if (ret is MethodCallRequest) {
				MethodCallRequest call = (MethodCallRequest)ret;
				Object target = call.Object;
				if (ReferenceEquals(target, null)) throw new NullReferenceException("target");
				Type intf = call.Type ?? target.GetType();
				DebugLog("Remote call {0}.{1} on {2}", intf.FullName, call.MethodName, target);
				if (!intf.IsInstanceOfType(target)) throw new InvalidCastException("target");
				MethodInfo meth;
				if (call.MethodSignature != null) {
					meth = intf.GetMethod(call.MethodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, call.MethodSignature, null);
				} else {
					meth = intf.GetMethod(call.MethodName, BindingFlags.Instance | BindingFlags.Public);
				}
				if (meth == null) throw new NullReferenceException("method");
				Object[] args = call.Arguments;
				Object retval;
				if (meth.Name == "GetType" && meth == typeof(Object).GetMethod("GetType", Type.EmptyTypes)) {
					if (target.GetType().IsPublic) retval = target.GetType();
					else retval = intf;
				} else {
					retval = meth.Invoke(target, args);
				}
				//Todo: causes lots of redundant data transfers, do something about it!
				/*Object[] rargs = new Object[0];
				for (int i = 0; i < args.Length; i++) {
					if (args[i] == null) continue;
					if (args[i].GetType().IsArray) {
						Array.Resize(ref rargs, i + 1);
						rargs[i] = args[i];
					}
				}
				resp.ReturnArguments = rargs;*/
				return FixReturnType(retval, meth.ReturnType);
			} else if (ret is PropertyAccessRequest) {
				PropertyAccessRequest call = (PropertyAccessRequest)ret;
				Object target = call.Object;
				if (ReferenceEquals(target, null)) throw new NullReferenceException("target");
				Type intf = call.Type ?? target.GetType();
				DebugLog("Remote property access {0}.{1} on {2}", intf.FullName, call.PropertyName, target);
				if (!intf.IsInstanceOfType(target)) throw new InvalidCastException("target");
				PropertyInfo meth = intf.GetProperty(call.PropertyName, BindingFlags.Instance | BindingFlags.Public);
				if (meth == null) throw new NullReferenceException("property");
				if (call.SetValue) {
					meth.SetValue(target, call.Value, null);
					return null;
				} else {
					return FixReturnType(meth.GetValue(target, null), meth.PropertyType);
				}
			} else {
				throw new InvalidDataException("Unexpected object type");
			}
		}
		private Object ProcessRemoteCallRequestA(Object ret) {
			if (ret is DelegateCallRequest || ret is MethodCallRequest || ret is PropertyAccessRequest) {
				MethodCallResponse resp = new MethodCallResponse();
				try {
					resp.ReturnValue = ProcessRemoteMethodCallRequestA(ret);
				} catch (Exception ex) {
					resp.Exception = ex;
					ErrorLog(ex);
				}
				return resp;
			} else if (ret is GetRootRequest) {
				DebugLog("Remote root request");
				return LocalRoot;
			} else if (ret is ObjectCanCastToRequest) {
				ObjectCanCastToRequest ctr = (ObjectCanCastToRequest)ret;
				Type intf = ctr.Type;
				Object target = ctr.Object;
				DebugLog("Remote type check for {0} on {1}", intf.Name, target);
				return intf != null && intf.IsInstanceOfType(target);
			} else if (ret is CreateStreamRequest) {
				CreateStreamRequest csr = (CreateStreamRequest)ret;
				StreamChannel ss = new StreamChannel(this, csr.StreamID);
				ss.OtherSide = csr.StreamObject;
				lock (streamChannels) streamChannels.Add(csr.StreamID, ss);
				return ss;
			} else {
				throw new InvalidDataException("Unexpected object type");
			}
		}
		#endregion

		#region Outgoing calls
		UInt32 callID = 0;
		private UInt32 AllocatePendingCallID(PendingRemoteCall call) {
			UInt32 cid;
			lock (pendingCalls) {
				while (true) {
					cid = ++callID;
					if (!pendingCalls.ContainsKey(cid)) break;
					Monitor.Wait(pendingCalls);
				}
				call.ID = cid;
				pendingCalls.Add(cid, call);
			}
			return cid;
		}
		private Object SyncCall(Object req) {
			UInt32 previousCall;
			Boolean hasPreviousCall;
			lock (waitingCallThreads) hasPreviousCall = waitingCallThreads.TryGetValue(Thread.CurrentThread, out previousCall);
			PendingRemoteCall pending = new PendingRemoteCall() { Completed = false, WaitHandle = new ManualResetEvent(false), CallData = req };
			AllocatePendingCallID(pending);
			SynchronousCall call = new SynchronousCall() { IsResult = false, CallID = pending.ID, Data = req, HasPreviousCallID = false };
			if (hasPreviousCall) {
				call.HasPreviousCallID = true;
				call.PreviousCallID = previousCall;
			}
			SendObject(call);
			while (true) {
				pending.WaitHandle.WaitOne();
				if (pending.Completed) break;
				pending.WaitHandle.Reset();
				if (pending.NextCall == null) throw new InvalidOperationException("Operation did not complete");
				SynchronousCall sc = pending.NextCall.Value;
				pending.NextCall = null;
				ProcessRemoteCallRequest(sc);
			}
			pending.WaitHandle.Close();
			if (pending.Error != null) throw pending.Error;
			return pending.Response;
		}
		private void AsyncCall(Object req) {
			UInt32 cid = AllocatePendingCallID(new PendingRemoteCall() { Completed = false, CallData = req });
			SendObject(new SynchronousCall() { IsResult = false, CallID = cid, Data = req, HasPreviousCallID = false });
		}

		public Object RemoteRoot {
			get {
				return SyncCall(new GetRootRequest());
			}
		}

		public static void AsyncCall(Delegate f, params Object[] args) {
			IProxyBase proxy;
			if ((proxy = GetRealProxyForObject(f)) != null) {
				proxy.Manager.AsyncCall(new DelegateCallRequest() { Delegate = proxy, Arguments = args });
			} else if ((proxy = GetRealProxyForObject(f.Target)) != null) {
				MethodCallRequest call = new MethodCallRequest() { Object = proxy, Type = f.Method.DeclaringType, MethodName = f.Method.Name, Arguments = args };
				call.MethodSignature = ConvertMethodParameterTypeArray(f.Method);
				proxy.Manager.AsyncCall(call);
			} else {
				throw new InvalidOperationException("Delegate is not a proxy for a remote object");
			}
		}

		private static Type[] ConvertMethodParameterTypeArray(MethodInfo method) {
			ParameterInfo[] parameters = method.GetParameters();
			Type[] types = new Type[parameters.Length];
			for (int i = 0; i < types.Length; i++) types[i] = parameters[i].ParameterType;
			return types;
		}
		private void ProxyCallCheckReturnType(Object value, Type type) {
			if (type == typeof(void)) return;
			if (type.IsInstanceOfType(value)) return;
			if (value == null && !type.IsValueType) return;
			throw new InvalidCastException("Type returned by remote procedure does not match expected type.");
		}
		private void ProxyCallFixReturnArguments(Object[] args, Object[] rargs) {
			if (rargs == null) return;
			for (int i = 0; i < rargs.Length; i++) {
				if (rargs[i] == null) continue;
				if (args[i] != null && args[i].GetType().IsArray) {
					((Array)rargs[i]).CopyTo((Array)args[i], 0);
				}
			}
		}
		private Object ProxyMakeCallDelegate(DelegateProxy obj, Object[] args) {
			DelegateCallRequest call = new DelegateCallRequest() { Delegate = obj, Arguments = args };
			MethodCallResponse resp = (MethodCallResponse)SyncCall(call);
			if (resp.Exception != null) throw new Exception("Remote exception", resp.Exception);
			ProxyCallFixReturnArguments(args, resp.ReturnArguments);
			ProxyCallCheckReturnType(resp.ReturnValue, obj.MethodSignature.ReturnType);
			return resp.ReturnValue;
		}
		private Object ProxyMakeCall(IProxyBase obj, MethodInfo method, Object[] args) {
			/*if (args.Length == 1 && method.ReturnType == typeof(void) && args[0] != null && args[0] is Delegate) {
				foreach (EventInfo ei in type.GetEvents()) {
					if (ei.EventHandlerType.IsInstanceOfType(args[0])) {
						if (method == ei.GetAddMethod()) {
							Console.WriteLine("ADD EVENT");
						} else if (method == ei.GetRemoveMethod()) {
							Console.WriteLine("REMOVE EVENT");
						}
					}
				}
			}*/
			MethodCallRequest call = new MethodCallRequest() { Object = obj, Type = method.DeclaringType, MethodName = method.Name, Arguments = args };
			call.MethodSignature = ConvertMethodParameterTypeArray(method);
			MethodCallResponse resp = (MethodCallResponse)SyncCall(call);
			if (resp.Exception != null) throw new Exception("Remote exception", resp.Exception);
			ProxyCallFixReturnArguments(args, resp.ReturnArguments);
			ProxyCallCheckReturnType(resp.ReturnValue, method.ReturnType);
			return resp.ReturnValue;
		}
		private Boolean ProxyCanCastTo(IProxyBase obj, Type type) {
			return (Boolean)SyncCall(new ObjectCanCastToRequest() { Object = obj, Type = type });
		}
		private void ProxyReleaseObject(ProxyObjectReference objref) {
			if (objref == null) return;
			ProxyReleaseObject(objref, objref.RefCnt);
		}
		private void ProxyReleaseObject(ProxyObjectReference objref, int refcnt) {
			lock (ObjectReferencesByID) {
				int newrefcnt = objref.Release(refcnt);
				IObjectReferenceBase regobjref;
				if (newrefcnt <= 0 && ObjectReferencesByID.TryGetValue(objref.ID, out regobjref) && objref == regobjref) ObjectReferencesByID.Remove(objref.ID);
			}
			try {
				if (Closed) return;
				SendObject(new ReferenceReleaseRequest() { ObjectID = objref.RemoteID, ReferenceCount = refcnt });
			} catch (Exception) { } //Assume the exception happened because the connection is closed. Otherwise memory leak...
		}

		class PendingRemoteCall {
			public UInt32 ID = 0;
			public ManualResetEvent WaitHandle = null;
			public Object Response = null;
			public Exception Error = null;
			public Boolean Completed = false;
			public SynchronousCall? NextCall = null;
			public Object CallData = null;

			public void SetError(Exception error) {
				this.Error = error;
				Completed = true;
				if (WaitHandle != null) WaitHandle.Set();
			}
			public void SetResult(Object result) {
				this.Response = result;
				Completed = true;
				if (WaitHandle != null) WaitHandle.Set();
			}
			public Boolean SetNextCall(SynchronousCall nextcall) {
				if (WaitHandle == null) return false;
				this.NextCall = nextcall;
				WaitHandle.Set();
				return true;
			}
		}
		#endregion

		#region Object serialization support
		private void Serialize(Stream stream, Object obj) {
			BinaryWriter writer = new BinaryWriter(stream);
			Serialize(writer, obj);
			writer.Flush();
		}
		private void Serialize(BinaryWriter writer, Object obj) {
			if (ReferenceEquals(obj, null)) {
				writer.Write((Byte)0);
			} else if (GetRealProxyForObject(obj) != null) {
				GetObjectData(obj, writer);
			} else {
				Type type = obj.GetType();
				if (type == typeof(Boolean)) {
					writer.Write((Byte)1);
					writer.Write((Boolean)obj);
				} else if (type == typeof(Byte)) {
					writer.Write((Byte)2);
					writer.Write((Byte)obj);
				} else if (type == typeof(Char)) {
					writer.Write((Byte)3);
					writer.Write((Int16)obj);
				} else if (type == typeof(Decimal)) {
					writer.Write((Byte)4);
					writer.Write((Decimal)obj);
				} else if (type == typeof(Double)) {
					writer.Write((Byte)5);
					writer.Write((Double)obj);
				} else if (type == typeof(Int16)) {
					writer.Write((Byte)6);
					writer.Write((Int16)obj);
				} else if (type == typeof(Int32)) {
					writer.Write((Byte)7);
					writer.Write((Int32)obj);
				} else if (type == typeof(Int64)) {
					writer.Write((Byte)8);
					writer.Write((Int64)obj);
				} else if (type == typeof(SByte)) {
					writer.Write((Byte)9);
					writer.Write((SByte)obj);
				} else if (type == typeof(Single)) {
					writer.Write((Byte)10);
					writer.Write((Single)obj);
				} else if (type == typeof(String)) {
					writer.Write((Byte)11);
					writer.Write((String)obj);
				} else if (type == typeof(UInt16)) {
					writer.Write((Byte)12);
					writer.Write((UInt16)obj);
				} else if (type == typeof(UInt32)) {
					writer.Write((Byte)13);
					writer.Write((UInt32)obj);
				} else if (type == typeof(UInt64)) {
					writer.Write((Byte)14);
					writer.Write((UInt64)obj);
				} else if (type == typeof(DateTime)) {
					writer.Write((Byte)32);
					writer.Write((Int64)((DateTime)obj).ToBinary());
				} else if (type.IsSubclassOf(typeof(Type))) {
					writer.Write((Byte)165);
					SerializeType(writer, (Type)obj);
				} else if (type.IsArray) {
					writer.Write((Byte)164);
					Array arr = (Array)obj;
					writer.Write((int)arr.Length);
					SerializeType(writer, type.GetElementType());
					for (int i = 0; i < arr.Length; i++) {
						Serialize(writer, arr.GetValue(i));
					}
				} else if (type.IsPrimitive) {
					throw new NotSupportedException();
				} else if (typeof(Delegate).IsAssignableFrom(type) || type.IsMarshalByRef) {
					GetObjectData(obj, writer);
				} else if (obj is ISerializable) {
					SerializationInfo si = new SerializationInfo(type, new FormatterConverter());
					((ISerializable)obj).GetObjectData(si, new StreamingContext(StreamingContextStates.All));
					writer.Write((Byte)128);
					SerializeType(writer, Type.GetType(si.FullTypeName + "," + si.AssemblyName));
					writer.Write((int)si.MemberCount);
					foreach (SerializationEntry se in si) {
						writer.Write(se.Name);
						Serialize(writer, se.Value);
					}
				} else if (type.IsSerializable) {
					MemberInfo[] members = FormatterServices.GetSerializableMembers(type);
					Object[] values = FormatterServices.GetObjectData(obj, members);
					writer.Write((Byte)128);
					SerializeType(writer, type);
					writer.Write((int)members.Length);
					for (int i = 0; i < members.Length; i++) {
						writer.Write(members[i].Name);
						Serialize(writer, values[i]);
					}
				} else {
					GetObjectData(obj, writer);
				}
			}
		}
		private void SerializeType(BinaryWriter writer, Type t) {
			writer.Write(t.FullName);
			writer.Write(t.Assembly.FullName);
		}
		private Object Deserialize(Stream stream) {
			BinaryReader reader = new BinaryReader(stream);
			return Deserialize(reader);
		}
		private Object Deserialize(BinaryReader reader) {
			Byte t = reader.ReadByte();
			if (t == 0) return null;
			if (t == 1) return reader.ReadBoolean();
			if (t == 2) return reader.ReadByte();
			if (t == 3) return (Char)reader.ReadInt16();
			if (t == 4) return reader.ReadDecimal();
			if (t == 5) return reader.ReadDouble();
			if (t == 6) return reader.ReadInt16();
			if (t == 7) return reader.ReadInt32();
			if (t == 8) return reader.ReadInt64();
			if (t == 9) return reader.ReadSByte();
			if (t == 10) return reader.ReadSingle();
			if (t == 11) return reader.ReadString();
			if (t == 12) return reader.ReadUInt16();
			if (t == 13) return reader.ReadUInt32();
			if (t == 14) return reader.ReadUInt64();
			if (t == 32) return DateTime.FromBinary(reader.ReadInt64());
			if (t == 128) {
				Type type = DeserializeType(reader);
				int cnt = reader.ReadInt32();
				Object inst;
				StreamingContext sc = new StreamingContext(StreamingContextStates.All);
				if (typeof(ISerializable).IsAssignableFrom(type) && type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }, null) != null) {
					SerializationInfo si = new SerializationInfo(type, new FormatterConverter());
					for (int i = 0; i < cnt; i++) {
						String name = reader.ReadString();
						Object value = Deserialize(reader);
						si.AddValue(name, value);
					}
					inst = Activator.CreateInstance(type, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Object[] { si, sc }, null);
				} else if (type.IsSerializable) {
					inst = FormatterServices.GetUninitializedObject(type);
					List<MemberInfo> members = new List<MemberInfo>();
					List<Object> values = new List<object>();
					for (int i = 0; i < cnt; i++) {
						String mname = reader.ReadString();
						Object value = Deserialize(reader);
						MemberInfo[] mms = type.GetMember(mname, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (mms.Length != 1) throw new InvalidOperationException();
						members.Add(mms[0]);
						values.Add(value);
					}
					FormatterServices.PopulateObjectMembers(inst, members.ToArray(), values.ToArray());
				} else {
					throw new InvalidOperationException("Type " + type.Name + " is not serializable");
				}
				IObjectReference objref = inst as IObjectReference;
				if (objref != null) inst = objref.GetRealObject(sc);
				return inst;
			}
			if (t == 129 || t == 130 || t == 131) {
				return SetObjectData(t, reader);
			}
			if (t == 164) {
				int len = reader.ReadInt32();
				Type type = DeserializeType(reader); Array arr = Array.CreateInstance(type, len);
				for (int i = 0; i < len; i++) arr.SetValue(Deserialize(reader), i);
				return arr;
			}
			if (t == 165) {
				return DeserializeType(reader);
			}
			throw new NotSupportedException();
		}
		private Type DeserializeType(BinaryReader reader) {
			String name = reader.ReadString();
			String aname = reader.ReadString();
			Type t = Type.GetType(name + ", " + aname, false);
			if (t != null) return t;
			foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) {
				t = assembly.GetType(name, false);
				if (t != null) return t;
			}
			return t;
		}

		Dictionary<UInt32, IObjectReferenceBase> ObjectReferencesByID = new Dictionary<uint, IObjectReferenceBase>();
		List<RemoteObjectReference> RemoteObjectReferences = new List<RemoteObjectReference>();
		uint remoteReferenceIndex = 0;
		void GetObjectData(object obj, BinaryWriter w) {
			IObjectReferenceBase rref = null;
			IProxyBase proxy = GetRealProxyForObject(obj);
			if (proxy != null && proxy.Manager == this) {
				rref = proxy.ObjectReference;
				w.Write((Byte)131);
				w.Write((UInt32)rref.RemoteID);
			} else {
				Boolean isDelegate = obj is Delegate;
				lock (ObjectReferencesByID) {
					foreach (IObjectReferenceBase objref in RemoteObjectReferences) {
						Object other = objref.Target;
						if (ReferenceEquals(other, obj) || (isDelegate && Delegate.Equals(other, obj))) {
							rref = objref;
							break;
						}
					}
					if (rref == null) {
						UInt32 objid;
						lock (ObjectReferencesByID) {
							while (true) {
								remoteReferenceIndex++;
								objid = remoteReferenceIndex;
								if ((objid & 0x80000000) != 0) {
									remoteReferenceIndex = 0;
									continue;
								}
								if (!ObjectReferencesByID.ContainsKey(objid)) break;
								Monitor.Wait(ObjectReferencesByID);
							}
						}
						rref = new RemoteObjectReference(objid, obj);
						ObjectReferencesByID.Add(objid, rref);
						RemoteObjectReferences.Add((RemoteObjectReference)rref);
					}
					rref.AddRef();
				}
				if (isDelegate) {
					w.Write((Byte)130);
					w.Write((UInt32)rref.RemoteID);
					SerializeType(w, obj.GetType());
				} else {
					w.Write((Byte)129);
					w.Write((UInt32)rref.RemoteID);
				}
			}
		}
		object SetObjectData(Byte t, BinaryReader r) {
			UInt32 objid = r.ReadUInt32();
			Type deltype = null;
			if (t == 130) deltype = DeserializeType(r);
			lock (ObjectReferencesByID) {
				Object target = null;
				IObjectReferenceBase rref;
				if (ObjectReferencesByID.TryGetValue(objid, out rref)) target = rref.Target;
				if (t == 131) {
					if (target == null) throw new InvalidOperationException("Object not found");
				} else {
					if (target == null) {
						if ((objid & 0x80000000) == 0) throw new InvalidOperationException("The Object ID is invalid");
						IProxyBase proxy;
						if (t == 130) {
							proxy = CreateDelegateProxy(objid, deltype);
						} else {
							String[] iftypes = null; //(String[])info.GetValue("InterfaceTypes", typeof(String[]));
							proxy = CreateObjectProxy(objid, iftypes);
						}
						rref = proxy.ObjectReference;
						ObjectReferencesByID[objid] = rref;
						target = proxy.GetTransparentProxy();
					}
					rref.AddRef();
				}
				return target;
			}
		}
		#endregion

		#region RPC messages
		[Serializable]
		struct SynchronousCall {
			public UInt32 CallID;
			public Boolean IsResult;
			public Object Data;
			public Boolean HasPreviousCallID;
			public UInt32 PreviousCallID;
		}

		[Serializable]
		struct MethodCallRequest {
			public Object Object;
			public Type Type;
			public String MethodName;
			public Type[] MethodSignature;
			public Object[] Arguments;
		}
		[Serializable]
		struct MethodCallResponse {
			public Exception Exception;
			public Object ReturnValue;
			public Object[] ReturnArguments;
		}
		[Serializable]
		struct PropertyAccessRequest {
			public Object Object;
			public Type Type;
			public String PropertyName;
			public Boolean SetValue;
			public Object Value;
		}
		[Serializable]
		struct ReferenceReleaseRequest {
			public UInt32 ObjectID;
			public Int32 ReferenceCount;
		}
		[Serializable]
		struct GetRootRequest {
		}
		[Serializable]
		struct ObjectCanCastToRequest {
			public Object Object;
			public Type Type;
		}
		[Serializable]
		struct CreateStreamRequest {
			public StreamChannel StreamObject;
			public int StreamID;
		}
		[Serializable]
		struct DelegateCallRequest {
			public Object Delegate;
			public Object[] Arguments;
		}
		#endregion

		#region Proxy magic
		static IProxyBase GetRealProxyForObject(Object obj) {
			if (RemotingServices.IsTransparentProxy(obj)) return RemotingServices.GetRealProxy(obj) as FWProxy;
			IProxyBase obj_IProxyBase = obj as IProxyBase;
			if (obj_IProxyBase != null) return obj_IProxyBase;
			Delegate obj_Delegate = obj as Delegate;
			if (obj_Delegate != null) {
				DelegateProxy pb = obj_Delegate.Target as DelegateProxy;
				if (pb != null) return pb;
			}
			return null;
		}
		public static RemotingManager GetManagerForObjectProxy(Object obj) {
			IProxyBase prox = GetRealProxyForObject(obj);
			if (prox == null) return null;
			return prox.Manager;
		}

		interface IObjectReferenceBase {
			UInt32 ID { get; }
			Object Target { get; }
			int AddRef();
			int RefCnt { get; }
			Boolean IsLocal { get; }
			UInt32 RemoteID { get; }
		}

		class RemoteObjectReference : IObjectReferenceBase {
			int refcnt = 0;
			public UInt32 ID { get; private set; }
			public Object Target { get; private set; }
			public int RefCnt { get { return refcnt; } }
			public int AddRef() { return Interlocked.Increment(ref refcnt); }
			public int Release(int count) { return Interlocked.Add(ref refcnt, -count); }
			public Boolean IsLocal { get { return true; } }
			public UInt32 RemoteID { get { return ID | 0x80000000; } }
			public RemoteObjectReference(UInt32 id, Object obj) {
				if ((id & 0x80000000) != 0) throw new InvalidOperationException("The Object ID is invalid");
				this.ID = id;
				this.Target = obj;
			}
		}

		interface IProxyBase {
			RemotingManager Manager { get; }
			UInt32 ID { get; }
			ProxyObjectReference ObjectReference { get; }
			Object GetTransparentProxy();
		}
		class ProxyObjectReference : IObjectReferenceBase {
			int refcnt = 0;
			WeakReference targetref;
			public UInt32 ID { get; private set; }
			public RemotingManager Manager { get; private set; }
			public Object Target {
				get {
					IProxyBase proxy = this.Proxy;
					if (proxy == null) return null;
					return proxy.GetTransparentProxy();
				}
			}
			public IProxyBase Proxy {
				get {
					if (targetref == null) return null;
					return (IProxyBase)targetref.Target; 
				} 
			}
			public int RefCnt { get { return refcnt; } }
			public int AddRef() {
				int newcnt = Interlocked.Increment(ref refcnt);
				if (newcnt > 10000 && Interlocked.CompareExchange(ref refcnt, 1, newcnt) == newcnt) {
					Manager.ProxyReleaseObject(this, newcnt - 1);
					newcnt = 1;
				}
				return newcnt;
			}
			public int Release(int count) { return Interlocked.Add(ref refcnt, -count); }
			public Boolean IsLocal { get { return false; } }
			public UInt32 RemoteID { get { return ID & 0x7FFFFFFF; } }
			public ProxyObjectReference(IProxyBase proxy) {
				this.Manager = proxy.Manager;
				this.ID = proxy.ID;
				if ((ID & 0x80000000) == 0) throw new InvalidOperationException("The Object ID is invalid");
				targetref = new WeakReference(proxy);
			}
		}
		class DelegateProxy : IProxyBase {
			public ProxyObjectReference ObjectReference { get; private set; }
			public RemotingManager Manager { get; private set; }
			public UInt32 ID { get; private set; }
			public Delegate Target { get; private set; }
			public MethodInfo MethodSignature { get; private set; }
			public Object GetTransparentProxy() { return Target; }

			public DelegateProxy(RemotingManager manager, UInt32 objid, MethodInfo methodinfo, Type delegateType, DynamicMethod methodBuilder) {
				this.Manager = manager;
				this.ID = objid;
				this.MethodSignature = methodinfo;
				Delegate mi = methodBuilder.CreateDelegate(delegateType, this);
				ObjectReference = new ProxyObjectReference(this);
			}
			~DelegateProxy() {
				Manager.ProxyReleaseObject(ObjectReference);
			}
			private Object DoCall(Object[] args) {
				return Manager.ProxyMakeCallDelegate(this, args);
			}
		}
		class FWProxy : RealProxy, IRemotingTypeInfo, IProxyBase {
			public RemotingManager Manager { get; private set; }
			public UInt32 ID { get; private set; }
			public ProxyObjectReference ObjectReference { get; private set; }

			public FWProxy(RemotingManager manager, UInt32 objid) : base(typeof(MarshalByRefObject)) {
				this.Manager = manager;
				this.ID = objid;
				this.ObjectReference = new ProxyObjectReference(this);
			}

			~FWProxy() {
				Manager.ProxyReleaseObject(ObjectReference);
			}

			public string TypeName { get; set; }

			public override IMessage Invoke(IMessage msg) {
				IMethodCallMessage methodCallMessage = msg as IMethodCallMessage;
				if (methodCallMessage != null) {
					Object r = Manager.ProxyMakeCall(this, (MethodInfo)methodCallMessage.MethodBase, methodCallMessage.Args);
					return new ReturnMessage(r, null, 0, null, methodCallMessage);
				}
				throw new NotImplementedException();
			}

			public bool CanCastTo(Type fromType, object o) {
				if (fromType == typeof(ISerializable)) return false;
				return Manager.ProxyCanCastTo(this, fromType);
			}
		}
		class ProxyBase : IProxyBase {
			public RemotingManager Manager { get; private set; }
			public UInt32 ID { get; private set; }
			public ProxyObjectReference ObjectReference { get; private set; }
			public Object GetTransparentProxy() { return this; }

			public void Init(RemotingManager manager, UInt32 objid) {
				this.Manager = manager;
				this.ID = objid;
				this.ObjectReference = new ProxyObjectReference(this);
			}

			protected Object DoCall(RuntimeMethodHandle methodh, Object[] args) {
				MethodInfo meth = (MethodInfo)MethodInfo.GetMethodFromHandle(methodh);
				return Manager.ProxyMakeCall(this, meth, args);
			}

			~ProxyBase() {
				Manager.ProxyReleaseObject(ObjectReference);
			}
		}

		private IProxyBase CreateDelegateProxy(UInt32 objid, Type deltype) {
			MethodInfo newMethod = deltype.GetMethod("Invoke");
			ParameterInfo[] parameters = newMethod.GetParameters();
			Type[] mparams = ArrayUtil.Merge(new Type[1] { typeof(DelegateProxy) }, Array.ConvertAll(parameters, delegate(ParameterInfo pi) { return pi.ParameterType; }));
			DynamicMethod methodBuilder = new DynamicMethod(String.Empty, newMethod.ReturnType, mparams, typeof(DelegateProxy));
			ILGenerator ilGenerator = methodBuilder.GetILGenerator();
			GenerateProxyMethodCode(ilGenerator, parameters, newMethod.ReturnType, typeof(DelegateProxy), "DoCall", null);
			return new DelegateProxy(this, objid, newMethod, deltype, methodBuilder);
		}

		private IProxyBase CreateObjectProxy(UInt32 objid, String[] typeNames) {
			DebugLog("Create proxy for remote object {0}", objid);
			IProxyBase proxy;
			if (true) {
				proxy = new FWProxy(this, objid);
			} else {
				Type[] types = new Type[typeNames.Length];
				int j = 0;
				for (int i = 0; i < typeNames.Length; i++) {
					Type t = Type.GetType(typeNames[i], false);
					if (t == null || !t.IsInterface) continue;
					types[j] = t;
					j++;
				}
				Array.Resize(ref types, j);
				Type proxyType = GetProxyType(types);
				proxy = (ProxyBase)Activator.CreateInstance(proxyType);
				((ProxyBase)proxy).Init(this, objid);
			}
			return proxy;
		}

		private static ModuleBuilder moduleBuilder = null;
		private static Dictionary<String, Type> proxyCache = null;
		private static Type GetProxyType(Type[] interfaceTypes) {
			Type proxyType;
			String key = String.Join("&", Array.ConvertAll(interfaceTypes, delegate(Type t) { return t.Name; }));
			lock (typeof(RemotingManager)) if (proxyCache == null) proxyCache = new Dictionary<String, Type>();
			lock (proxyCache) {
				if (!proxyCache.TryGetValue(key, out proxyType)) {
					proxyType = GenerateProxyType(key, interfaceTypes);
					proxyCache.Add(key, proxyType);
				}
			}
			return proxyType;
		}
		private static Type GenerateProxyType(String name, Type[] interfaceTypes) {
			lock (typeof(RemotingManager)) {
				if (moduleBuilder == null) {
					AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("UCIS.Remoting.Proxies"), AssemblyBuilderAccess.Run);
					moduleBuilder = assembly.DefineDynamicModule("UCIS.Remoting.Proxies", false);
				}
			}
			TypeBuilder typeBuilder = moduleBuilder.DefineType(
				name.Length == 0 ? "UndefinedProxy" : name, //mono does not like types with no name!
				TypeAttributes.NotPublic | TypeAttributes.Sealed,
				typeof(ProxyBase),
				interfaceTypes);
			foreach (Type interfaceType in interfaceTypes) {
				foreach (MethodInfo method in interfaceType.GetMethods()) {
					GenerateProxyMethod(typeBuilder, method);
				}
			}
			return typeBuilder.CreateType();
		}
		private static void GenerateProxyMethod(TypeBuilder typeBuilder, MethodInfo newMethod) {
			if (newMethod.IsGenericMethod) newMethod = newMethod.GetGenericMethodDefinition();
			ParameterInfo[] parameters = newMethod.GetParameters();
			Type[] parameterTypes = Array.ConvertAll(parameters, delegate(ParameterInfo parameter) { return parameter.ParameterType; });

			MethodBuilder methodBuilder = typeBuilder.DefineMethod(
				"Impl_" + newMethod.DeclaringType.Name + "_" + newMethod.Name,
				MethodAttributes.Public | MethodAttributes.Virtual,
				newMethod.ReturnType,
				parameterTypes);
			typeBuilder.DefineMethodOverride(methodBuilder, newMethod);

			if (newMethod.IsGenericMethod) {
				methodBuilder.DefineGenericParameters(Array.ConvertAll(newMethod.GetGenericArguments(), delegate(Type type) { return type.Name; }));
			}

			ILGenerator ilGenerator = methodBuilder.GetILGenerator();
			GenerateProxyMethodCode(ilGenerator, parameters, newMethod.ReturnType, typeof(ProxyBase), "DoCall", newMethod);
		}
		private static void GenerateProxyMethodCode(ILGenerator ilGenerator, ParameterInfo[] parameters, Type returnType, Type baseType, String baseMethod, MethodInfo methodRef) {
			LocalBuilder localBuilder = ilGenerator.DeclareLocal(typeof(Object[]));
			ilGenerator.Emit(OpCodes.Ldc_I4, parameters.Length);
			ilGenerator.Emit(OpCodes.Newarr, typeof(Object));
			ilGenerator.Emit(OpCodes.Stloc, localBuilder);
			for (int i = 0; i < parameters.Length; i++) {
				if (parameters[i].ParameterType.IsByRef) continue;
				ilGenerator.Emit(OpCodes.Ldloc, localBuilder);
				ilGenerator.Emit(OpCodes.Ldc_I4, i);
				ilGenerator.Emit(OpCodes.Ldarg, i + 1);
				if (parameters[i].ParameterType.IsValueType) ilGenerator.Emit(OpCodes.Box, parameters[i].ParameterType);
				ilGenerator.Emit(OpCodes.Stelem_Ref);
			}
			ilGenerator.Emit(OpCodes.Ldarg_0);
			if (methodRef != null) ilGenerator.Emit(OpCodes.Ldtoken, methodRef);
			ilGenerator.Emit(OpCodes.Ldloc, localBuilder);
			ilGenerator.Emit(OpCodes.Call, baseType.GetMethod(baseMethod, BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic));
			if (returnType == typeof(void)) {
				ilGenerator.Emit(OpCodes.Pop);
			} else if (returnType.IsValueType) {
				ilGenerator.Emit(OpCodes.Unbox_Any, returnType);
			}
			ilGenerator.Emit(OpCodes.Ret);
		}
		#endregion
	}
}