.Net Remoting 应用实例 DotNetRemotingChat

最近研究了一下.Net Remoting技术,做了个实例,简单记录下来,以便参考。

概念性的东西不多说了,前面也转载了几篇文章,写的都不错,这里主要说说我的这个实例的实现过程。

这个实例包含三个项目:Chat_Server,Chat_Client,Chat_CommonLib,即服务端,客户端和公共程序集,一般.Net Remoting都包含这三块。

先来看看Chat_CommonLib项目,该项目是一个类库程序,包含两个接口IRemoteObjectFactory,IRemoteObject,一个类EventWrapper和一个公共委托MessagedEventHandler。

namespace Chat_CommonLib
{
    public interface IRemoteObjectFactory
    {
        IRemoteObject CreateInstance(string user);
    }
}


 

namespace Chat_CommonLib
{
    public delegate void MessagedEventHandler(string msg);

    public interface IRemoteObject
    {
        string User { get; set; }
        void Connect();
        void Disconnect();
        void SendMessage(string msg);
        void SubMessage(string msg);
        event MessagedEventHandler Messaged;
    }
}


namespace Chat_CommonLib
{
    public class EventWrapper:MarshalByRefObject
    {
        public event MessagedEventHandler Messaged;

        public void SubMessage(string msg)
        {
            if (Messaged != null)
            {
                MessagedEventHandler temp = null;
                foreach (Delegate del in Messaged.GetInvocationList())
                {
                    try
                    {
                        temp = (MessagedEventHandler)del;
                        temp(msg);
                    }
                    catch (Exception ex)
                    {
                        //RemoteObjectFactory.SubLogger("Send event message fail.\r\n" + ex.ToString());
                    }
                }
            }
        }
        public override object InitializeLifetimeService()
        {
            return null;
        }
    }
}


在服务端实现远程对象,注意远程对象及其工厂都要继承MarshalByRefObject,并实现先前定义的接口

namespace Chat_Server.Codes
{
    class RemoteObjectFactory : MarshalByRefObject, IRemoteObjectFactory
    {
        public static event Action<string> Logger;
        public static List<IRemoteObject> RemoteObjects = new List<IRemoteObject>();

        public IRemoteObject CreateInstance(string user)
        {
            IRemoteObject remoteObject = new RemoteObject(user);
            RemoteObjects.Add(remoteObject);
            return remoteObject;
        }
        public static void SubLogger(string msg)
        {
            if (Logger != null)
            {
                Logger(msg);
            }
        }
        public static void SendMessage(IRemoteObject user, string msg)
        {
            user.SubMessage(msg);
        }
        public static void SendMessage(IRemoteObject user, string msg, bool self)
        {
            foreach (IRemoteObject _user in RemoteObjects)
            {
                if (_user != user || self)
                {
                    SendMessage(_user, msg);
                }
            }
        }
        public override object InitializeLifetimeService()
        {
            return null;
        }
    }
}


 

namespace Chat_Server.Codes
{
    class RemoteObject : MarshalByRefObject, IRemoteObject
    {
        public event MessagedEventHandler Messaged;
        private string mUser;
        public string User
        {
            get
            {
                return mUser;
            }
            set
            {
                mUser = value;
            }
        }

        public RemoteObject(string user)
        {
            mUser = user;
        }

        public void Connect()
        {
            string msg = string.Format("{0}\tConnected.", mUser);
            RemoteObjectFactory.SubLogger(msg);
            RemoteObjectFactory.SendMessage(this, "Server connected.");
            RemoteObjectFactory.SendMessage(this, msg, false);
        }

        public void Disconnect()
        {
            string msg = string.Format("{0}\tDisconnected.", mUser);
            RemoteObjectFactory.RemoteObjects.Remove(this);
        }
        public void SubMessage(string msg)
        {
            if (Messaged != null)
            {
                MessagedEventHandler temp = null;
                foreach (Delegate del in Messaged.GetInvocationList())
                {
                    try
                    {
                        temp = (MessagedEventHandler)del;
                        temp(msg);
                    }
                    catch (Exception ex)
                    {
                        RemoteObjectFactory.SubLogger("Send event message fail.\r\n" + ex.ToString());
                    }
                }
            }
        }
        public override object InitializeLifetimeService()
        {
            return null;
        }


        public void SendMessage(string msg)
        {
            RemoteObjectFactory.SendMessage(this, this.User + "\t" + msg, true);
        }
    }
}


 注册通道,我这里采用服务端SingleTon激活方式,但是功能上看类似于客户端激活方式,因为远程对象实在客户端实例化的,并且每个客户端独立使用一个远程对象。

服务端通道注册:

private bool CreateRemoteObject()
        {
            try
            {
                SoapServerFormatterSinkProvider soap = new SoapServerFormatterSinkProvider();
                BinaryServerFormatterSinkProvider binary = new BinaryServerFormatterSinkProvider();
                soap.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
                binary.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
                soap.Next = binary;
                Hashtable table = new Hashtable();
                table.Add("port", II_ServerPort);
                TcpChannel channel = new TcpChannel(table, null, soap);
                ChannelServices.RegisterChannel(channel, false);

                RemotingConfiguration.ApplicationName = "RemotingChat";
                RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemoteObjectFactory), "RemotingChat", WellKnownObjectMode.Singleton);
            }
            catch (Exception ex)
            {
                IS_Error = ex.ToString();
                return false;
            }
            return true;
        }


 

客户端通道注册及远程对象的激活:

private bool MakeConnection()
        {
            try
            {
                SoapServerFormatterSinkProvider soap = new SoapServerFormatterSinkProvider();
                BinaryServerFormatterSinkProvider binary = new BinaryServerFormatterSinkProvider();
                soap.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
                binary.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
                soap.Next = binary;
                Hashtable table = new Hashtable();
                table.Add("port", "0");
                TcpChannel channel = new TcpChannel(table, null, soap);
                ChannelServices.RegisterChannel(channel, false);

                string url = string.Format("tcp://{0}:{1}/RemotingChat", IS_ServerIP, II_ServerPort);
                IRemoteObjectFactory objectFactory = (IRemoteObjectFactory)Activator.GetObject(typeof(IRemoteObjectFactory), url);
                I_RemoteObject = objectFactory.CreateInstance(GetCurrentIP());
                EventWrapper eventWrapper = new EventWrapper();
                eventWrapper.Messaged += new MessagedEventHandler(eventWrapper_Messaged);
                I_RemoteObject.Messaged += new MessagedEventHandler(eventWrapper.SubMessage);
                I_RemoteObject.Connect();
            }
            catch (Exception ex)
            {
                IS_Error = ex.ToString();
                return false;
            }
            return true;
        }


 主要代码就如以上所述,下面重点说说其中要注意的几点:

1、工厂模式,工厂模式是一种极为常见的编程方式,其核心就一个CreateInstance方式,能够返回指定类型的实例,在这个实例中RemoteObject就工厂RemoteObjectFactory返回的一个对象,可以在客户端调用这个方法。

2、代码组织,由于远程对象在服务端和客户端都要引用,一个好的办法就是利用接口,在接口中定义远程对象的各个方法事件等,在服务端具体实现,而客户端只需引用对应的接口就行,并且接口和其他可能需要封送的的类都放在公共程序集中,这样代码更加清晰,引用也方便。

3、远程对象事件及事件的订阅,这是最难也是最容易出错的地方,尤其客户端订阅服务端事件。客户端是通过代理引用远程对象的,因而客户端引用的并不是真正的远程对象,而是其代理,所以对其订阅事件并不能这真订阅到远程对象本身。一个好的办法就是包装事件,即设计一个事件包装器类EventWrapper,其中的事件和远程对象的定义样,这个类还必须继承MarshalByRefObject以实现远程封送,通过这个中间类可以实现客户端订阅服务端事件。

发布了41 篇原创文章 · 获赞 9 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/jian200801/article/details/7990359