C# IPC管道通信模塊

  平時都是在網上找水喝,今天我也當回打井人。上週在搞IPC通信的項目,一個星期下來,以爲仍是有些收穫的,在此總結一下開發中我遇到的問題,但願能爲其餘人提供一些經驗。socket

  首先說說IPC通信有什麼特色。第一,就是IPC只能用於同一臺PC的多個進程間的通信,這個是一個前提,若是想在多臺電腦上進行通信,那仍是老老實實的用socket或管道來作。第二,簡單。相比於socket,IPC通信的準備工做不多。server端創建一個有名字的IPC通道,註冊通道實例,再給這個通道指定一個共享數據類型和對應的實例,就算OK啦,夠簡單吧。client端也是同樣的道理,首先創建一個管道,註冊,引用通道的共享數據實例,如今就能夠通信了。第三,因爲僅限於PC內部進程間的通信,因此不須要綁定端口,而且通信速度和穩定性都很好。速度和穩定性都很好理解,一臺機器,都是人民內部矛盾,因此處理起來基本沒問題。不須要綁定到硬件資源就能夠避免硬件資源的衝突,想象一下,若是用socket的話就要綁定一個監聽端口,若是是多個實例在一臺機器上運行,這個端口該怎麼分配,就是一個比較麻煩問題啦。ide

  再說說IPC的通信載體,共享數據實例。咱們能夠想象成是sever和client共用的一個盒子,這個盒子的格局由數據類型肯定,須要傳遞什麼數據,均可以經過聲明共享數據類型來肯定。這個裏面有個事情要注意,就是共享數據的實例是在server或client一端創建的,因此用來傳數據是能夠的,可是不能用來傳遞數據地址。由於這個共享數據只能在一個進程中被建立,在另個進程中引用,若是進程A把本身的地址傳給進程B,進程B就會很是鬱悶,由於進程B沒有這個地址。這就好像一個超市裏有1到100號儲物箱,可是一我的打開儲物箱發現讓他到200號箱裏去取東西是同樣的。函數

  基本的東西說完了,如今看看具體的實現,限於篇幅關係,只貼基本的東西,其餘的內容能夠看附件中的代碼:測試

  共享數據模塊:this

  public class DataOperator : MarshalByRefObjectorm

 {server

       // Data進程

        private string InformationStr = string.Empty;事件

        // eventip

        public event ClientHasConnectedHandler OnConnectedEvent = null;

        public event WriteOptionFinishedHandler OnServerWriteEvent = null;

        public event WriteOptionFinishedHandler OnClientWriteEvent = null;

        public event ClientHasConnectedHandler OnDisconnectedEvent = null;

// client call this function to implement connected event

        public void SendConnectEventByClient(string ipcName){

            try{

                if (OnConnectedEvent != null)

                    OnConnectedEvent(ipcName);

            }

            catch(Exception ex)

            {

                IPCLog.WriteLog(string.Format("error msg is {0}, DTid = {1}", ex.Message, id));

            }

        }

        #region Buffer Operator about

        public bool Writeflag{

           get{

          if (InformationStr == string.Empty) return true;

          else return false;

          }

        }

        public string Write{

            set {

            InformationStr = string.Empty;

            InformationStr = value;}

        }

        public string Read{

            get{

                string tmpRes = InformationStr;

                InformationStr = string.Empty;

                return tmpRes;}

        }

        #endregion

    }

  共享數據類中主要包括兩個部分,一是用來傳遞數據的InformationStr,一是用來傳遞控制狀態的事件。前面說過要想用IPC傳遞數據是很是容易的,可是咱們不能老是循環去判斷數據段裏是否是有數據,因此須要用事件進行控制。以DataOperator 類中的OnConnectedEvent爲例,在server端綁定該事件,在client端調用SendConnectEventByClient方法,server端就知道,有一個client創建了鏈接。

  再看server端的處理:

BinaryServerFormatterSinkProvider serverProvider =

                   new BinaryServerFormatterSinkProvider();

                serverProvider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;

                IDictionary props = new Hashtable();

                props["name"] = m_ipcName;

                props["portName"] = m_ipcName;

                props["typeFilterLevel"] = TypeFilterLevel.Full;

                // create a server channel

                svrChl = new IpcServerChannel(props, serverProvider, null);

                ChannelServices.RegisterChannel(svrChl);            

                m_optDt = new DataOperator(m_ipcName);

                objRef = RemotingServices.Marshal(m_optDt, m_ipcName + "_DataOperator");

                m_optDt.OnConnectedEvent += new ClientHasConnectedHandler(ClientHasConnected);

  這裏解釋一下,serverProvider實例用來設定序列化格式,因爲咱們須要在共享數據中增長事件類型,因此須要重指定序列化等級。props用來設定通道信息,若是沒有特殊的要求能夠直接使用IpcServerChannel的構造函數來設定通道信息。RemotingServices.Marshal方法用來把m_optDt創建一個引用名,這樣,其餘的進程就能夠經過這個名字引用到server創建的這個實例。

  最後client端:

  // client channel

  IDictionary props = new Hashtable();

  props["name"] = m_ipcName;

  props["typeFilterLevel"] = TypeFilterLevel.Full;

  props["portName"] = m_ipcName;

  // create a new client channel

  cltChl = new IpcClientChannel(props, null);

  ChannelServices.RegisterChannel(cltChl);

 // Get DataOperator instance of server

  m_optDt = (DataOperator)Activator.GetObject(typeof(DataOperator), chlName);

  m_optDt.SendConnectEventByClient(this.IPCName);

  client端相對比較簡單,不作過多解釋了。如今就能夠在經過調用DataOperator的Write和Read屬性發信和收信了。固然如今功能只能算是完成了一半,由於如今只能由server監控client的動做,若是須要client也響應server的動做,就須要在作一條通道,只要server端和client端方向調換一下就OK了。

  最後再說一下通道的釋放。當通道的歷史使命完成後,須要把通道釋放,固然直接退出程序是能夠的。代碼以下:

 RemotingServices.Disconnect(m_optDt);

 // unregister all channels

 ChannelServices.UnregisterChannel(svrChl);

 代碼很簡單,就兩句話,須要注意的是第一句。RemotingServices.Disconnect用來釋放咱們綁定的共享數據引用。當咱們註銷通道以後,共享數據的引用是不會自動消失的,須要手動釋放,不然下次再創建鏈接的時候,RemotingServices.Marshal會絕不客氣地告訴你,你指定的名字已經有人用啦。

  附件裏提供了完整的代碼和測試測序,可以實現server和client的雙向通訊及狀態控制,IPC通訊模塊作了封裝,server端能夠創建多個通道與不通的client端通訊並區分數據來源。

相關文章
相關標籤/搜索