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远程类所激活的事件都应该是子线程的吧?怎么我这没报错(子线程不能操作主线程的控件)呢?

你可能感兴趣的:(net)