Skip to content

Instantly share code, notes, and snippets.

@Alxandr
Created August 24, 2011 11:22
Show Gist options
  • Save Alxandr/1167823 to your computer and use it in GitHub Desktop.
Save Alxandr/1167823 to your computer and use it in GitHub Desktop.
A small framework for creating proxy-classes and send data using XML to and from server/client.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Xml;
using NirQ.Common.Proxy;
using System.Net;
using System.Diagnostics;
using System.Threading;
using System.Xml.Serialization;
namespace NirQ.Common.Transport
{
public class CommandConnection : MessageConnection
{
private volatile Dictionary<Guid, ManualResetEvent> requestWaitDict = new Dictionary<Guid, ManualResetEvent>();
private volatile Dictionary<Guid, Object> requestReturnDict = new Dictionary<Guid, Object>();
private readonly object dictLock = new object();
private XmlSerializer serializer = new XmlSerializer(typeof(MethodMessage));
private List<Type> typeList = new List<Type>();
public event EventHandler<MethodEventArgs> Command;
public CommandConnection(Socket socket)
: base(socket)
{
}
public CommandConnection(IPEndPoint ipEndPoint)
: base(ipEndPoint)
{
}
public void RegisterTypes(params Type[] t)
{
typeList.AddRange(t.Where(ty => ty != typeof(void)));
serializer = new XmlSerializer(typeof(MethodMessage), typeList.ToArray());
}
public void SendCommand(string commandName, object[] parameters = null)
{
parameters = parameters ?? new object[0];
MemoryStream ms = new MemoryStream();
MethodMessage sm = new MethodMessage()
{
Method = commandName,
Parameters = parameters,
Return = false
};
serializer.Serialize(ms, sm);
string xml = Encoding.UTF8.GetString(ms.ToArray());
SendMessage(xml);
}
public object SendRequest(string commandName, object[] parameters = null)
{
parameters = parameters ?? new object[0];
MemoryStream ms = new MemoryStream();
//SoapFormatter sf = new SoapFormatter();
//sf.TypeFormat = FormatterTypeStyle.TypesAlways;
MethodMessage sm = new MethodMessage()
{
Method = commandName,
Parameters = parameters,
Return = true
};
serializer.Serialize(ms, sm);
string xml = Encoding.UTF8.GetString(ms.ToArray());
ManualResetEvent resetEvent = new ManualResetEvent(false);
lock (dictLock)
{
requestWaitDict.Add(sm.Id, resetEvent);
}
SendMessage(xml);
resetEvent.WaitOne();
Object ret = null;
lock (dictLock)
{
requestWaitDict.Remove(sm.Id);
if (requestReturnDict.ContainsKey(sm.Id))
{
ret = requestReturnDict[sm.Id];
requestReturnDict.Remove(sm.Id);
}
}
return ret;
}
public void SendReturn(Guid id, object ret)
{
SendCommand("$Return", new object[] { id, ret });
}
protected override void OnMessage(StringEventArgs eventArgs)
{
base.OnMessage(eventArgs);
//SoapFormatter sf = new SoapFormatter();
//sf.TypeFormat = FormatterTypeStyle.TypesAlways;
byte[] bytes = Encoding.UTF8.GetBytes(eventArgs.ToString());
MemoryStream ms = new MemoryStream(bytes);
try
{
MethodMessage sm = serializer.Deserialize(ms) as MethodMessage;
if (sm.Method == "$Return")
OnReturn(sm);
else
OnCommand(new MethodEventArgs(sm.Method, sm.Parameters, sm.Id, sm.Return));
}
catch (Exception e)
{
Debug.Fail("Failed to deserialize", e.Message);
}
}
protected virtual void OnReturn(MethodMessage message)
{
if (message.Parameters.Length < 1)
throw new ArgumentException("Not enough parameters.");
Guid id = (Guid)message.Parameters[0];
if (message.Parameters.Length == 2)
{
lock (dictLock)
{
requestReturnDict.Add(id, message.Parameters[1]);
}
}
ManualResetEvent evt;
lock (dictLock)
{
evt = requestWaitDict[id];
}
evt.Set();
}
protected virtual void OnCommand(MethodEventArgs eventArgs)
{
if (Command != null)
Command(this, eventArgs);
}
}
}
using System;
using System.Reflection;
namespace NirQ.Common.Proxy
{
public interface IProxyHandler
{
object OnMethod(MethodInfo method, object[] args);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Diagnostics;
using System.Net;
namespace NirQ.Common.Transport
{
public class MessageConnection : TcpConnection
{
private Queue<String> messageQueue = new Queue<String>();
private readonly object messageQueueLock = new object();
private ManualResetEvent messageWaiting = new ManualResetEvent(false);
private Thread messageSendingThread;
private bool messageSendingThreadExitOnNext = false;
private StringBuilder inMessageBuffer = new StringBuilder();
public event EventHandler<StringEventArgs> Message;
public event EventHandler<StringEventArgs> MessageSent;
public MessageConnection(Socket socket)
: base(socket)
{
messageSendingThread = new Thread(new ThreadStart(StartMessageSender));
messageSendingThread.Name = "MessageSender";
messageSendingThread.Start();
}
public MessageConnection(IPEndPoint ipEndPoint)
: base(ipEndPoint)
{
messageSendingThread = new Thread(new ThreadStart(StartMessageSender));
messageSendingThread.Name = "MessageSender";
messageSendingThread.Start();
}
public void SendMessage(String message)
{
message = message.Replace("\r\n", "\n\r");
lock (messageQueueLock)
{
messageQueue.Enqueue(message + "\r\n");
}
SendMessages();
}
private void SendMessages()
{
messageWaiting.Set();
}
private void StartMessageSender()
{
while (true)
{
bool newMessage = false;
lock (messageQueueLock)
{
newMessage = messageQueue.Count != 0;
}
if (!newMessage)
{
messageWaiting.WaitOne();
Debug.WriteLine("Newmessage-flag received", this.GetType().FullName);
}
else
{
String message;
lock (messageQueueLock)
{
message = messageQueue.Dequeue();
}
Send(Encoding.UTF8.GetBytes(message));
OnMessageSent(new StringEventArgs(message));
messageWaiting.Reset();
}
if (messageSendingThreadExitOnNext)
break;
}
}
protected override void OnReceive(DataEventArgs eventArgs)
{
base.OnReceive(eventArgs);
string message = Encoding.UTF8.GetString(eventArgs.Data);
if (!message.Contains("\r\n"))
{
inMessageBuffer.Append(message);
return;
}
inMessageBuffer.Append(message);
message = inMessageBuffer.ToString();
inMessageBuffer = new StringBuilder();
while (message.Contains("\r\n"))
{
string command = message.Substring(0, message.IndexOf("\r\n"));
message = message.Substring(message.IndexOf("\r\n") + 2);
message = message.Replace("\n\r", "\r\n");
StringEventArgs args = new StringEventArgs(command);
if(!String.IsNullOrEmpty(command))
OnMessage(args);
}
inMessageBuffer.Append(message);
}
protected virtual void OnMessage(StringEventArgs eventArgs)
{
if (Message != null)
Message(this, eventArgs);
}
protected virtual void OnMessageSent(StringEventArgs eventArgs)
{
if (MessageSent != null)
MessageSent(this, eventArgs);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NirQ.Common.Proxy
{
public class MethodEventArgs : EventArgs
{
private string methodName;
private object[] arguments;
private bool ret;
private Guid id;
public MethodEventArgs(string methodName, object[] arguments, Guid id, bool ret)
{
this.methodName = methodName;
this.arguments = arguments;
this.id = id;
this.ret = ret;
}
public string MethodName
{
get { return methodName; }
}
public object[] Arguments
{
get { return arguments; }
}
public Type[] ArgumentTypes
{
get { return arguments.Select(a => a.GetType()).ToArray(); }
}
public bool Return
{
get { return ret; }
}
public Guid Id
{
get { return id; }
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NirQ.Common.Transport
{
[Serializable]
public class MethodMessage
{
public string Method { get; set; }
public object[] Parameters { get; set; }
public bool Return { get; set; }
public Guid Id { get; set; }
public MethodMessage()
{
Id = Guid.NewGuid();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NirQ.Common.Proxy;
using System.Net.Sockets;
using System.Net;
using System.Reflection;
using System.Diagnostics;
using System.Threading;
namespace NirQ.Common.Transport
{
public class ProxyCommandConnection<TIn, TOut> : CommandConnection, IProxyHandler
{
private ProxyGenerator<TIn> proxy;
private TOut callbackObject;
private Type outType = typeof(TOut);
public TIn Object
{
get { return proxy.Object; }
}
public ProxyCommandConnection(Socket socket, TOut callbackObject)
: base(socket)
{
proxy = new ProxyGenerator<TIn>(this);
this.callbackObject = callbackObject;
if (!typeof(TOut).IsInterface)
throw new ArgumentException("TOut mus be an interface.");
if (callbackObject == null)
throw new ArgumentNullException("callbackObject can't be null.");
Type[] types = GetReturnTypes(typeof(TIn), typeof(TOut));
RegisterTypes(types);
}
public ProxyCommandConnection(IPEndPoint ipEndPoint, TOut callbackObject)
: base(ipEndPoint)
{
proxy = new ProxyGenerator<TIn>(this);
this.callbackObject = callbackObject;
if (!typeof(TOut).IsInterface)
throw new ArgumentException("TOut mus be an interface.");
if (callbackObject == null)
throw new ArgumentNullException("callbackObject can't be null.");
Type[] types = GetReturnTypes(typeof(TIn), typeof(TOut));
RegisterTypes(types);
}
private Type[] GetReturnTypes(params Type[] ts)
{
List<Type> types = new List<Type>();
foreach (Type t in ts)
foreach (var method in t.GetMethods())
if (!types.Contains(method.ReturnType))
types.Add(method.ReturnType);
return types.ToArray();
}
protected override void OnCommand(MethodEventArgs eventArgs)
{
base.OnCommand(eventArgs);
MethodInfo method = outType.GetMethod(eventArgs.MethodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase, null, eventArgs.ArgumentTypes, null);
if (method == null)
Debug.WriteLine("Invalid command " + eventArgs.MethodName + " with " + eventArgs.Arguments.Length + " arguments.",
this.GetType().FullName);
else
ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>
{
object ret = method.Invoke(callbackObject, eventArgs.Arguments);
if(eventArgs.Return)
SendReturn(eventArgs.Id, ret);
}));
}
public object OnMethod(MethodInfo method, object[] args)
{
return SendRequest(method.Name, args);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
namespace NirQ.Common.Proxy
{
public class ProxyGenerator<T>
{
private static volatile bool built;
private static AssemblyBuilder assemblyBuilder;
private static ModuleBuilder moduleBuilder;
private static readonly object buildLock = new object();
private static Dictionary<String, Type> typeCache = new Dictionary<String, Type>();
private TypeBuilder typeBuilder;
private T obj;
public ProxyGenerator(IProxyHandler handler)
{
Type t = typeof(T);
if (!t.IsInterface)
throw new ArgumentException("T needs to be an interface");
lock (buildLock)
{
if (!built)
{
assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName("NirQ.Proxies"),
AssemblyBuilderAccess.Run);
string assemblyName = assemblyBuilder.GetName().Name;
moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName);
}
built = true;
}
string typeName = String.Format("{0}_NirQ_proxy", t.Name);
Type result;
if (typeCache.Keys.Contains(typeName))
{
result = typeCache[typeName];
}
else
{
typeBuilder = moduleBuilder.DefineType(
String.Format("{0}_NirQ_proxy", t.Name),
TypeAttributes.Class | TypeAttributes.Public,
typeof(Object),
new Type[] { t });
Type objType = Type.GetType("System.Object");
ConstructorInfo objCtor = objType.GetConstructor(new Type[] { });
FieldBuilder proxyFieldBuilder = typeBuilder.DefineField("handler", typeof(IProxyHandler), FieldAttributes.Private);
ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[] { typeof(IProxyHandler) });
ILGenerator cIl = constructorBuilder.GetILGenerator();
cIl.Emit(OpCodes.Ldarg_0); // Load this to stack
cIl.Emit(OpCodes.Call, objCtor); // Call base (object) constructor
cIl.Emit(OpCodes.Ldarg_0); // Load this to stack
cIl.Emit(OpCodes.Ldarg_1); // Load the IProxyHandler to stack
cIl.Emit(OpCodes.Stfld, proxyFieldBuilder); // Set proxy to the actual proxy
cIl.Emit(OpCodes.Ret);
MethodInfo callRetMethod = typeof(IProxyHandler).GetMethod("OnMethod", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
null,
new Type[] { typeof(MethodInfo), typeof(object[]) },
null);
foreach (MethodInfo mi in t.GetMethods())
{
MethodBuilder mb = typeBuilder.DefineMethod(mi.Name,
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, mi.ReturnType,
mi.GetParameters().Select(pi => pi.ParameterType).ToArray());
ILGenerator il = mb.GetILGenerator();
int privateParameterCount = mi.GetParameters().Length;
LocalBuilder argArray = il.DeclareLocal(typeof(object[]));
il.Emit(OpCodes.Ldc_I4, privateParameterCount);
il.Emit(OpCodes.Newarr, typeof(object));
il.Emit(OpCodes.Stloc, argArray);
LocalBuilder methodInfo = il.DeclareLocal(typeof(MethodInfo));
il.Emit(OpCodes.Ldtoken, mi);
il.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetMethodFromHandle", new Type[] { typeof(RuntimeMethodHandle) }));
il.Emit(OpCodes.Stloc, methodInfo);
for (int i = 0; i < mi.GetParameters().Length; i++)
{
ParameterInfo info = mi.GetParameters()[i];
il.Emit(OpCodes.Ldloc, argArray);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldarg_S, i + 1);
if (info.ParameterType.IsPrimitive || info.ParameterType.IsValueType)
il.Emit(OpCodes.Box, info.ParameterType);
il.Emit(OpCodes.Stelem_Ref);
}
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, proxyFieldBuilder);
il.Emit(OpCodes.Ldloc, methodInfo);
il.Emit(OpCodes.Ldloc, argArray);
il.Emit(OpCodes.Call, callRetMethod);
if (mi.ReturnType.IsValueType && mi.ReturnType != typeof(void))
il.Emit(OpCodes.Unbox_Any, mi.ReturnType);
if (mi.ReturnType == typeof(void))
il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Ret);
}
result = typeBuilder.CreateType();
typeCache.Add(typeName, result);
}
object o = Activator.CreateInstance(result, new object[] { handler });
obj = (T)o;
}
public T Object
{
get { return obj; }
}
private void SendException(ILGenerator il)
{
il.Emit(OpCodes.Ldstr, "This is a test.");
il.Emit(OpCodes.Newobj, typeof(Exception).GetConstructor(new Type[] { typeof(string) }));
il.Emit(OpCodes.Throw);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment