Click here to Skip to main content
15,892,537 members
Articles / Programming Languages / C#

Using Web Services for Remoting over the Internet.

Rate me:
Please Sign up or sign in to vote.
4.76/5 (38 votes)
15 Feb 2002CPOL8 min read 372K   4.2K   222  
This article describes a design and implementation (C#) of the Remoting over Internet using the Web Service as a gateway into the Remoting infrastructure.
//======================================================================================================
//	The library for Custom Remoting via Web Service channel (Sender).
//	(C) Copyright 2002, Roman Kiss (rkiss@pathcom.com)
//	All rights reserved.
//	The code and information is provided "as-is" without waranty of any kind, either expresed or implied.
//------------------------------------------------------------------------------------------------------
//	History:
//			22-01-2002	RK	Initial Release	
//======================================================================================================
//
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Messaging;
using System.Collections;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;
using System.Xml;
using System.Xml.Serialization;
using System.Reflection;
using System.Messaging;
using System.Runtime.InteropServices;


namespace RKiss.WebServiceChannelLib
{
	public delegate void delegateAsyncWorker(IMessage msgReq, IMessageSink replySink); 

	// Sender
	public class Sender : IChannelSender
	{	
		// these are a hardcoded default values
		const int PATH_MINNUMOFFIELDS = 4;
		public const string DEFAULT_CHANNELMODE = "Binary";
		public const string DEFAULT_CHANNELNAME = "ws";
		public const int    DEFAULT_CHANNELPRIORITY = 1;
		//
		private string m_ChannelName;
		private int m_ChannelPriority;
		private string m_ChannelMode;

	
		// Sender
		public Sender() : 
			this(DEFAULT_CHANNELNAME, DEFAULT_CHANNELMODE, DEFAULT_CHANNELPRIORITY) {}
		public Sender(string channelName) : 
			this(channelName, DEFAULT_CHANNELMODE, DEFAULT_CHANNELPRIORITY) {}
		public Sender(string channelName, string mode) : 
			this(channelName, mode, DEFAULT_CHANNELPRIORITY) {}
		
		public Sender(string channelName, string mode, int priority)
		{
			Init(channelName, mode, priority);
		}
		public Sender(IDictionary properties, IServerChannelSinkProvider serverSinkProvider) 
		{
			string channelName = DEFAULT_CHANNELNAME;
			int channelPriority = DEFAULT_CHANNELPRIORITY;
			string channelMode = DEFAULT_CHANNELMODE;

			// administratively setup the config values (xxx.exe.config file)
			if(properties.Contains("name")) 
				channelName = properties["name"].ToString();
			if(properties.Contains("priority")) 
				channelPriority = Convert.ToInt32(properties["priority"]);
			if(properties.Contains("mode")) 
				channelMode = properties["mode"].ToString().ToUpper();
		
			Init(channelName, channelMode, channelPriority);
		}
		public void Init(string channelName, string mode, int priority)
		{
			m_ChannelName = channelName;
			m_ChannelPriority = priority;
			m_ChannelMode = mode;
			Trace.WriteLine(string.Format("SenderChannel[{0}] has been initiated.", m_ChannelName));
		}
		// IChannelSender
		public string ChannelName	{ get { return m_ChannelName; } 
		}
		public int ChannelPriority	{ get { return m_ChannelPriority; }	
		}
		public string Parse(string url, out string objectURI) { return objectURI = null; }

		// IChannelSender (activation)
		public virtual IMessageSink CreateMessageSink(String url, Object data, out string objectURI)
		{
			objectURI = null;
			string[] gtwpath = url.Split(new Char[]{';'});					// split gateway/RemoteObject
			string[] s = gtwpath[0].Split(new Char[]{'/'});

			if(s.Length < PATH_MINNUMOFFIELDS || m_ChannelName + ":" != s[0])
				return null;												// this is not correct channel
			
			string outpath = gtwpath[0].Replace(m_ChannelName, "http");		// web service
			objectURI = gtwpath[1].TrimStart(new char[]{' '});				// remote object
			WSMessageSink msgsink = new WSMessageSink(m_ChannelMode, objectURI, outpath);

			Trace.WriteLine(string.Format("SenderChannel[{0}] initiated the WSMessageSink for {1}.", m_ChannelName, objectURI));

			return msgsink;			
		}
	}//Sender



	// MSMQMessageSink
	public class WSMessageSink : IMessageSink, IDictionary
	{
		private string			m_ObjectUri;
		private string			m_outpath;
		private string			m_mode;
		
		// MSMQMessageSink
		public WSMessageSink(string mode, string objuri, string outpath) 
		{
			m_mode = mode;
			m_ObjectUri = objuri;
			m_outpath = outpath;
		}
		// helper
		// handler of the AsyncProcessMessage worker
		private void handlerAsyncWorker(IMessage msgReq, IMessageSink replySink) 
		{
			AsyncResult ar = replySink.NextSink as AsyncResult;
			// call Remote Method and wait for its Return Value 
			IMessage msgRsp = SyncProcessMessage(msgReq);
			// update AsyncResult state (_replyMsg, IsCompleted, waitstate, etc.)
			replySink.SyncProcessMessage(msgRsp);
		}
	
		// IMessageSink (MethodCall)
		public virtual IMessage SyncProcessMessage(IMessage msgReq)
		{	
			IMessage msgRsp = null;
			
			try 
			{
				msgReq.Properties["__Uri"] = m_ObjectUri;
				Service webservice = new Service(m_outpath); 
			
				if(m_mode == "SOAP") 
				{
					// serialize IMessage into the stream (SoapMessage)
					MemoryStream reqstream = new MemoryStream();
					SoapFormatter sf = new SoapFormatter();
					RemotingSurrogateSelector rss = new RemotingSurrogateSelector();
					rss.SetRootObject(msgReq);
					sf.SurrogateSelector = rss;
					sf.AssemblyFormat = FormatterAssemblyStyle.Full;
					sf.TypeFormat = FormatterTypeStyle.TypesAlways;
					sf.TopObject = new SoapMessage();
					sf.Serialize(reqstream, msgReq);
					ISoapMessage sm = sf.TopObject;
					reqstream.Position = 0;
					StreamReader sr = new StreamReader(reqstream);
					string request = sr.ReadToEnd();
					reqstream.Close();
					sr.Close();

					// call web service
					string respond = webservice.SyncProcessSoapMessage(request);

					// return messages
					StreamWriter rspsw = new StreamWriter(new MemoryStream());
					rspsw.Write(respond);
					rspsw.Flush();
					rspsw.BaseStream.Position = 0;
					ISoapMessage rspsoapmsg = (ISoapMessage)sf.Deserialize(rspsw.BaseStream);
					rspsw.Close();

					if(rspsoapmsg.ParamValues[0] is Exception) 
					{
						throw rspsoapmsg.ParamValues[0] as Exception;
					}
					else 
					{
						object returnVal = rspsoapmsg.ParamValues[4];
						object[] OutArgs = rspsoapmsg.ParamValues[5] as object[];
						LogicalCallContext lcc = rspsoapmsg.ParamValues[6] as LogicalCallContext;
						ReturnMessage rm = new ReturnMessage(
								returnVal,								//Object return
								OutArgs,								//Object[] outArgs
								OutArgs.Length,							//int outArgsCount
								lcc,									//LogicalCallContext callCtx
								msgReq as IMethodCallMessage			//IMethodCallMessage mcm
								);
						msgRsp = rm as IMessage;
					}
				}
				else 
				{
					msgReq.Properties["__Uri2"] = m_ObjectUri;			// workaround! 
					// serialize and encode IMessage
					BinaryFormatter bf = new BinaryFormatter();
					MemoryStream reqstream = new MemoryStream();
					bf.Serialize(reqstream, msgReq);
					reqstream.Position = 0;
					string request = Convert.ToBase64String(reqstream.ToArray());
					reqstream.Close();
				
					// call Web Service
					string respond = webservice.SyncProcessMessage(request);

					// decode and deserialize IMessage
					byte[] rspbyteArray = Convert.FromBase64String(respond);
					MemoryStream rspstream = new MemoryStream();
					rspstream.Write(rspbyteArray, 0, rspbyteArray.Length);
					rspstream.Position = 0;
					msgRsp = (IMessage)bf.Deserialize(rspstream);
					rspstream.Close();
				}
			}
			catch(Exception ex) 
			{
				Trace.WriteLine(string.Format("Client:SyncProcessMessage error = {0}", ex.Message));
				msgRsp = new ReturnMessage(ex, (IMethodCallMessage)msgReq);
			}
			
			return msgRsp;
		}

		public virtual IMessageCtrl AsyncProcessMessage(IMessage msgReq, IMessageSink replySink)
		{
			IMessageCtrl imc = null;

			if(replySink == null)										// OneWayAttribute
			{	
				Trace.WriteLine("Client-[OneWay]Async:CALL");
				SyncProcessMessage(msgReq);
			}
			else
			{
				Trace.WriteLine("Client-Async:CALL");
				// spawn thread (delegate work)
				delegateAsyncWorker daw = new delegateAsyncWorker(handlerAsyncWorker);
				daw.BeginInvoke(msgReq, replySink, null, null);
			}
			
			return imc;
		}
		        
		// IDictionary (not implemented)
		public virtual IMessageSink NextSink {	get { return null; }}
		public virtual bool Contains(Object key) { return false; }
		public virtual void Add(Object key, Object value){}
		public virtual void Remove(Object key){}
		public virtual void Clear()	{}
		public virtual IDictionaryEnumerator GetEnumerator() { return null;	}
		IEnumerator IEnumerable.GetEnumerator()	{ return null; }
		public int Count { get { return 0; }}
		public void CopyTo(Array array, int index){}
		public ICollection Keys { get { return null; }}
		public ICollection Values { get { return null; }}
		public Object SyncRoot { get { return null; }}
		public  bool IsReadOnly { get { return true; }}
		public  bool IsFixedSize { get { return true; }}
		public bool IsSynchronized { get { return true;	}}
		public Object this[Object key] { get { return null; } set {}}
	}// WSMessageSink
}//namespace RKiss.WebServiceChannelLib



By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions