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

    整个工程大概就是这样的,希望上面的思路不是很乱吧?

  • 相关阅读:
    浏览器内核、webview内核
    移动端(h5)页面适配
    vue 开发多页面应用
    git 指令
    joomla多语言建站之默认前台语言设置
    初识node,原理与浏览器何其相似
    vue-cli下配置项目访问ip和服务器ip
    js中不容小觑的var声明
    vue中的事件监听之——v-on vs .$on
    用js的eval函数模拟Web API中的onclick事件
  • 原文地址:https://www.cnblogs.com/yunxizfj/p/1688323.html
Copyright © 2011-2022 走看看