Created
August 24, 2011 11:22
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Reflection; | |
namespace NirQ.Common.Proxy | |
{ | |
public interface IProxyHandler | |
{ | |
object OnMethod(MethodInfo method, object[] args); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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