NET Remoting 双向通信(转)

 

最近有个项目,大致需求是,服务端发送消息通知客户端上传指定的数据,然后处理后一部分显示在服务端界面上。也是在网上胡乱搜索一片,看到一篇Remoting广播事件的博客+Remoting觉得这么还可以做。

大致原理是:通过服务端广播事件,客户端通过调用远程类将数据以参数的方式传给服务端,然后激活服务端界面层的事件就达到双向了。都是靠远程类里的2个事件,一个给服务端,一个给客户端,分别交叉执行。这就相当于: 服务端界面--远程类--客户端界面,远程类起到了一个中间人的作用样,是吧?

 

先看看服务端封装的Remoting的类

 

public sealed class RemotingServer
    {
        HttpChannel tcpC;

        
public static RemotingObject.Remoter Obj = null;

        
static RemotingServer instance = new RemotingServer();

        
public static RemotingServer GetRemotingServer
        {
            
get { return instance; }
        }

        RemotingServer()
        {
            
//tcpC = new TcpChannel(9000);
            
        }

        
public void ServerTakeOn()
        {
            
//if (!ExistServer(tcpC.ChannelName))
            
//{
                RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingObject.Remoter), "RemotingService", WellKnownObjectMode.Singleton);
                BinaryServerFormatterSinkProvider serverProvider 
= new BinaryServerFormatterSinkProvider();
                BinaryClientFormatterSinkProvider clientProvider 
= new BinaryClientFormatterSinkProvider();
                serverProvider.TypeFilterLevel 
= TypeFilterLevel.Full;
                IDictionary props 
= new Hashtable();
                props[
"port"= 9500;
                tcpC 
= new HttpChannel(props, clientProvider, serverProvider);
                ChannelServices.RegisterChannel(tcpC);

                Obj 
= new RemotingObject.Remoter();
                ObjRef objRef 
= RemotingServices.Marshal(Obj, "RemotingMessage.soap");
            
            
//}
        }

        
public void ServerTakeOff()
        {
            
if (ExistServer(tcpC.ChannelName))
            {
                tcpC.StopListening(
null);
                ChannelServices.UnregisterChannel(tcpC);
            }
        }

        
private bool ExistServer(string serverName)
        {
            
bool tmp_b = false;
            IChannel[] list 
= ChannelServices.RegisteredChannels;
            
foreach (IChannel ic in list)
            {
                HttpChannel tmp_tcp 
= (HttpChannel)ic;
                
if(tmp_tcp.ChannelName==serverName)
                {
                    tmp_b 
= true;
                    
break;
                }
            }
            
return tmp_b;
        }
    }

 

下面2句代码特别的重要是服务端界面能和客户端共同操作的远程类

 

Obj = new RemotingObject.Remoter();
ObjRef objRef 
= RemotingServices.Marshal(Obj, "RemotingMessage.soap");

 

接下来看看客户端的Remoting是怎么样的

 

public sealed class RemotingClient
    {

        HttpChannel tcp;

        IMessage imessage 
= null;
        EventWrapper wrapper 
= null;

        
string _mes;

        
public string ErrorMes
        {
            
get { return _mes; }
        }

        
public IMessage GetObject1
        {
            
get { return imessage; }
        }
        
public EventWrapper GetObject2
        {
            
get { return wrapper; }
        }

        
static RemotingClient _client = new RemotingClient();
        
public static RemotingClient GetObject
        {
            
get { return _client; }
        }
        RemotingClient()
        {
            
        }

        
public bool Connect(string serverIP)
        {
            
try
            {

                
//_object = (RemotingObject.Remoter)Activator.GetObject(typeof(RemotingObject.Remoter),
                
//    string.Format("tcp://{0}:9000/RemotingService", serverIP));
                
                BinaryServerFormatterSinkProvider serverProvider 
= new BinaryServerFormatterSinkProvider();
                BinaryClientFormatterSinkProvider clientProvider 
= new BinaryClientFormatterSinkProvider();
                serverProvider.TypeFilterLevel 
= TypeFilterLevel.Full;
                IDictionary props 
= new Hashtable();
                props[
"port"= 0;
                tcp 
= new HttpChannel(props, clientProvider, serverProvider);
                ChannelServices.RegisterChannel(tcp);

                
//RemotingConfiguration.RegisterActivatedClientType(typeof(IMessage), string.Format("tcp://{0}:9000/RemotingService", serverIP));
                imessage = (IMessage)Activator.GetObject(typeof(IMessage), string.Format("http://{0}:9500/RemotingMessage.soap", serverIP));
                wrapper 
= new EventWrapper();
                
                
return true;
            }
            
catch(Exception ex)
            {
                _mes 
= ex.Message;
                
return false;
            }
        }

        
public void UnConnect()
        {
            
if (ExistServer(tcp.ChannelName))
            {
                ChannelServices.UnregisterChannel(tcp);
            }
        }

        
private bool ExistServer(string serverName)
        {
            
bool tmp_b = false;
            IChannel[] list 
= ChannelServices.RegisteredChannels;
            
foreach (IChannel ic in list)
            {
                HttpChannel tmp_tcp 
= (HttpChannel)ic;
                
if (tmp_tcp.ChannelName == serverName)
                {
                    tmp_b 
= true;
                    
break;
                }
            }
            
return tmp_b;
        }
    }

 

 

是通过HTTP协议的,如果是域名的话,好像要先解析成IP吧?连接服务端,获取远程对象,给出属性返回让客户端界面能操作远程类

接下来就是客户端连接的代码,连接成功后,取出远程类,关联远程类的事件

 

       #region 连接
        
private void btnConnect_Click(object sender, EventArgs e)
        {
            
if (!client.Connect(tbxIp.Text))
            {
                MesBox.Show(client.ErrorMes, 
0);
                
return;
            }
            imessage 
= client.GetObject1;
            wrapper 
= client.GetObject2;
            
try
            {
                wrapper.SendMessageEven 
+= new SendMessageEvenHandler(GetTxt);
                imessage.SendMessageEven 
+= new SendMessageEvenHandler(wrapper.SendMessageing);
            }
            
catch (Exception ex)
            {
                MesBox.Show(ex.Message, 
0);
                
return;
            }
            btnConnect.Text 
= "已连接";
            btnConnect.Enabled 
= false;
        }
        
#endregion

 

 

       #region 服务端广播
        
private void GetTxt(string txt)
        {
            
if (txt == "A")
            {
                
string ip = GetlocalIP();
                List
<string> tmp = GetHardInfo();
                imessage.AddInfo(ip, tmp);
            }
        }
        
#endregion

 

 

上面代码中参数TXT==A,那是我自己胡弄的一个标识,不重要,然后获取客户端的IP和硬盘信息,通过调用AddInfo方法传给服务端原程类。

在远程类中有事件处理的方法,在AddInfo方法内会调用,那么关联服务端界面的方法就会被执行,数据也通过参数传递过去了

 

public class Remoter : MarshalByRefObject, IMessage
    {

        
#region IMessage 成员

        
public event SendMessageEvenHandler SendMessageEven;

        
public List<string> HardDeskInfo = new List<string>();

        
public void SendMessage( string txt)
        {
            
if (SendMessageEven != null)
            {
                SendMessageEvenHandler tmp_even 
= null;
                
foreach (Delegate dl in SendMessageEven.GetInvocationList())
                {
                    
try
                    {
                        tmp_even 
= (SendMessageEvenHandler)dl;
                        tmp_even(txt);
                    }
                    
catch
                    {
                        SendMessageEven 
-= tmp_even;
                    }
                }
            }
        }
        
public void AddInfo(string IP, List<string> hardinfo)
        {
            
string tmp = string.Empty;
            
foreach (string s in hardinfo)
            {
                
//获取剩余大小
                string tmp1 = s.Substring(s.LastIndexOf("@"+ 1);
                
//获取分区@总大小
                string tmp2 = s.Substring(0, s.Length - tmp1.Length - 1);
                
//获取总大小
                string tmp3 = tmp2.Substring(tmp2.LastIndexOf("@"+ 1);
                
//获取分区
                string tmp4 = tmp2.Substring(0, tmp2.Length - tmp3.Length - 1);
                
//格式是 IP@分区@总大小@剩余大小
                tmp = string.Format("{0}@{1}@{2}@{3}", IP, tmp4, tmp3, tmp1);
                HardDeskInfo.Add(tmp);
                ClientReciveData(tmp);
            }
        }
        
public void ClearInfo()
        {
            HardDeskInfo.Clear();
        }
        
public override object InitializeLifetimeService()
        {
            
return null;
        }
        
#endregion

        
#region 激活服务端事件
        
public delegate void ClientReciveDataEvenHandler(string txt);
        
public event ClientReciveDataEvenHandler ClientReciveDataEven;
        
public void ClientReciveData(string txt)
        {
            
if (ClientReciveDataEven != null)
            {
                ClientReciveDataEvenHandler tmp_even 
= null;
                
foreach (Delegate dl in ClientReciveDataEven.GetInvocationList())
                {
                    
try
                    {
                        tmp_even 
= (ClientReciveDataEvenHandler)dl;
                        tmp_even(txt);
                    }
                    
catch
                    {
                        ClientReciveDataEven 
-= tmp_even;
                    }
                }
            }
        }
        
#endregion

    }

 

 

上面这个就是原创类了,是继承于接口IMessage

 

namespace RemotingObject.Common
{
    
public delegate void SendMessageEvenHandler(string txtt);

    
public interface IMessage
    {
        
event SendMessageEvenHandler SendMessageEven;
        
void SendMessage(string txt);
        
void AddInfo(string IP, List<string> hardinfo);
    }
}

 

 

接口和远程类是不同的类库生成的DLL,在服务端2个都会被调用,但是客户端只会调用接口的DLL,中间还有一个事件适配器吧?是这么叫的吧?这个方式源于Remoting服务端事件广播

 

namespace RemotingObject.Common
{
    
public class EventWrapper : MarshalByRefObject
    {
        
public event SendMessageEvenHandler SendMessageEven;

        
public void SendMessageing( string txt)
        {
            SendMessageEven(txt);
        }

        
public override object InitializeLifetimeService()
        {
            
return null;
        }
    }
}

 

 

这2个的关系我到现在还没搞懂的,不过能使用它们就很不错啦

最后看看服务端界面方法关联的远程类事件

 

       #region 窗体加载
        
private void frmMain_Load(object sender, EventArgs e)
        {
            _server 
= RemotingServer.GetRemotingServer;
            _server.ServerTakeOn();
            RemotingServer.Obj.ClientReciveDataEven 
+= new RemotingObject.Remoter.ClientReciveDataEvenHandler(ClientReciveData);
        }
        
#endregion

        
#region 窗体关闭
        
private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            RemotingServer.Obj.ClientReciveDataEven 
-= new RemotingObject.Remoter.ClientReciveDataEvenHandler(ClientReciveData);
            _server.ServerTakeOff();
        }
        
#endregion

        
#region 远程类事件
        
private void ClientReciveData(string txt)
        {
            SetLst(txt);
        }
        
#endregion
       
#region 设置列表
        
private void SetLst(string tmp)
        {
            
string ip = string.Empty;
            
string dir = string.Empty;
            
string all = string.Empty;
            
string free = string.Empty;
            
//剩余大小
            string tmp1 = tmp.Substring(tmp.LastIndexOf("@"+ 1);
            
//IP@分区@总大小
            string tmp2 = tmp.Substring(0, tmp.Length - tmp1.Length - 1);
            
//总大小
            string tmp3 = tmp2.Substring(tmp2.LastIndexOf("@"+ 1);
            
//IP@分区
            string tmp4 = tmp2.Substring(0, tmp2.Length - tmp3.Length - 1);
            
//分区
            string tmp5 = tmp4.Substring(tmp4.LastIndexOf("@"+ 1);
            
//IP
            string tmp6 = tmp4.Substring(0, tmp4.Length - tmp5.Length - 1);

            ip 
= tmp6;
            dir 
= tmp5;
            all 
= tmp3;
            free 
= tmp1;

            ListViewItem itm 
= new ListViewItem();
            itm.SubItems[
0].Text = ip;
            itm.SubItems.Add(dir);
            itm.SubItems.Add(all);
            itm.SubItems.Add(free);

            lst.Items.Add(itm);
        }

 

接受到参数传递的数据,处理下放到ListView显示出来,不过我记得Remoting远程类所激活的事件都应该是子线程的吧?怎么我这没报错(子线程不能操作主线程的控件)呢?

posted @ 2011-09-22 13:58  gds111789  阅读(602)  评论(0编辑  收藏  举报